Spring IOC的創(chuàng)建原理

前言

先來回顧一下創(chuàng)建Spring IOC容器的兩種方式:

  1. 通過BeanFactory創(chuàng)建;
  2. 通過ApplicationContext創(chuàng)建芹壕;

其中ApplicationContext又可以分為XML和Annotation兩種形式丐谋,Annotation形式以AnnotationConfigApplicationContext子類為代表。

BeanFactory繼承結(jié)構(gòu)

BeanFactory繼承類圖
  • AutowireCapableBeanFactory接口定義IOC容器的自動(dòng)裝配功能,在ApplicationContext類中可以通過getAutowireCapableBeanFactory()獲取該接口膀跌,從而使得ApplicationContext擁有自動(dòng)裝配功能;
  • Hierarchical的意思是"分級(jí)"固灵,所以HierarchicalBeanFactory允許應(yīng)用啟動(dòng)多個(gè)BeanFactory并設(shè)置父子關(guān)系捅伤。例如Spring+SpringMVC應(yīng)用中的父子容器;
  • ListableBeanFactory中定義獲取多個(gè)Bean的方法巫玻,而BeanFactory中只定義了獲取單個(gè)Bean的方法丛忆。
  • ApplicationContext繼承了BeanFactory,同時(shí)擁有了AutowireCapableBeanFactory仍秤、HierarchicalBeanFactory熄诡、ListableBeanFactory三個(gè)接口定義的功能。
  • ConfigurableBeanFactory接口定義了ioc容器的可定制性诗力,它定義了設(shè)置類裝載器凰浮,屬性編輯器,容器初始化后置處理器等方法苇本;幾乎所有的beanFactory都會(huì)實(shí)現(xiàn)這個(gè)接口袜茧,賦予了BeanFactory可擴(kuò)展的功能。

ApplicationContext繼承結(jié)構(gòu)

ApplicationContext繼承類圖
  • ApplicationContext分為web環(huán)境類和非web環(huán)境類瓣窄,如果要在WEB容器中創(chuàng)建IOC需要使用WebApplicationContext的實(shí)現(xiàn)類笛厦。
  • FileSystemXmlApplicationContextClassPathXmlApplicationContext類都是通過XML文件創(chuàng)建IOC容器,不同的是要加載的文件位置不同康栈,前者位于文件系統(tǒng)中递递,后者位于classpath當(dāng)中。
  • AnnotationConfigApplicationContext是基于注解的形式來創(chuàng)建IOC容器啥么,采用java配置文件登舞。

我們將通過Spring(具體版本為:4.3.9.RELEASE)注解的方式來分析IOC容器的創(chuàng)建過程,主要通過AnnotationConfigApplicationContext類悬荣。入口程序如下

@Test
public void test(){
    AnnotationConfigApplicationContext applicationContext = new
          AnnotationConfigApplicationContext(MainConfig.class);
    System.out.println("IOC容器創(chuàng)建完畢");
    Object person = applicationContext.getBean("person");
    Person person1 = applicationContext.getBean(Person.class);
    System.out.println(person == person1);
}

其中MainConfig是Spring的注解配置類菠秒,其代碼如下

@Configuration
public class MainConfig {

    /**
     * @Bean注解的value屬性用來定義注冊(cè)到IOC容器中的bean name
     * 如果不指定value屬性,則用方法名作為bean name
     * @return
     */
    @Bean(value = "person")
    public Person getPerson(){
        Person person = new Person();
        person.setName("Jerry");
        person.setAge(18);
        person.setNickName("J");
        return person;
    }
}

AnnotationConfigApplicationContext的繼承體系

AnnotationConfigApplicationContext繼承體系圖

由上圖可知,AnnotationConfigApplicationContextApplicationContext接口某個(gè)具體實(shí)現(xiàn)類践叠,而ApplicationContextBeanFactory的子接口言缤。所以我們可以將AnnotationConfigApplicationContext看成是一種BeanFactory,他們的本質(zhì)都是一樣的禁灼。

ApplicationContext和BeanFactory都是用于加載Bean的管挟,這兩個(gè)類都可以看成是Spring IOC容器,但是ApplicationContext比BeanFactory提供了更多的擴(kuò)展功能弄捕。

常見的ApplicationContext


上圖中用紅框框起來的是一些常見的ApplicationContext僻孝,它們用于各種不同的場(chǎng)景。在這些類實(shí)例化的過程中守谓,都會(huì)直接或間接的調(diào)用AbstractApplicationContext.refresh()方法穿铆。而AbstractApplicationContext.refresh()方法的執(zhí)行過程就是IOC容器的創(chuàng)建過程。下面來分析一下refresh()方法的執(zhí)行過程斋荞。

refresh()執(zhí)行流程

refresh()方法很重要荞雏,它是各種IOC容器初始化的入口。首先需要知道的是refresh()方法運(yùn)用了“模板方法”的設(shè)計(jì)模式平酿,后面會(huì)出個(gè)專欄分析Spring中運(yùn)用的設(shè)計(jì)模式凤优。先來看看refresh()的源碼

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 準(zhǔn)備上下文信息
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory.
        // 2. 初始化BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 3. 對(duì)BeanFactory進(jìn)行功能填充
        prepareBeanFactory(beanFactory);

        try {
            // 4. 子類覆蓋的方法,用于做特殊的處理,這是個(gè)鉤子方法
            postProcessBeanFactory(beanFactory);
            // 5. 激活各種BeanFactoryPostProcessor處理器
            invokeBeanFactoryPostProcessors(beanFactory);
            // 6. 注冊(cè)各種Bean的后置處理器染服,后置處理器的調(diào)用發(fā)生在getBean
            registerBeanPostProcessors(beanFactory);
            // 7. 國(guó)際化處理
            initMessageSource();
            // 8. 在容器中初始化消息廣播器
            initApplicationEventMulticaster();
            // 9. 子類覆蓋的方法别洪,各?類來初始化其他的bean叨恨,鉤子方法
            onRefresh();
            // 10. 注冊(cè)Listener到消息廣播器中
            registerListeners();
            // 11. 初始化剩余的所有非懶加載的單實(shí)例bean
            finishBeanFactoryInitialization(beanFactory);
            // 12. 發(fā)送ContextRefreshEvent廣播柳刮,完成刷新過程
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            // 13. 清除緩存
            resetCommonCaches();
        }
    }
}

refresh()正常執(zhí)行完畢,即表明創(chuàng)建好IOC容器痒钝。分析源碼可以知道refresh()方法做了13件事秉颗。

  1. 準(zhǔn)備上下文信息;
  2. 創(chuàng)建BeanFactory送矩;
  3. 對(duì)BeanFactory進(jìn)行功能填充蚕甥;
  4. 子類覆蓋的方法,用于做特殊的處理,這是個(gè)鉤子方法栋荸;
  5. 激活各種BeanFactoryPostProcessor處理器菇怀;
  6. 注冊(cè)各種Bean的后置處理器,后置處理器的調(diào)用發(fā)生在getBean晌块;
  7. 國(guó)際化處理爱沟;
  8. 在容器中初始化消息廣播器;
  9. 子類覆蓋的方法匆背,各?類來初始化其他的bean呼伸,鉤子方法;
  10. 注冊(cè)Listener到消息廣播器中钝尸;
  11. 初始化剩余的所有非懶加載的單實(shí)例bean括享;
  12. 發(fā)送ContextRefreshEvent廣播搂根,完成刷新過程;
  13. 清除緩存铃辖。

1. 準(zhǔn)備上下文信息

protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    if (logger.isInfoEnabled()) {
        logger.info("Refreshing " + this);
    }

    // 初始化property資源剩愧,擴(kuò)展點(diǎn),留給子類實(shí)現(xiàn)
    initPropertySources();

    // 驗(yàn)證需要的配置文件是否加載到環(huán)境中
    // see ConfigurablePropertyResolver#setRequiredProperties
    getEnvironment().validateRequiredProperties();

    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

2. 創(chuàng)建BeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

這個(gè)方法執(zhí)行完畢之后娇斩,ApplicationContext就擁有了BeanFactory的功能隙咸。這段代碼做了兩件事:2.1 初始化BeanFactory;2.2 獲取BeanFactory成洗。

2.1 初始化BeanFactory

refreshBeanFactory();用來初始化BeanFactory五督,它最終調(diào)用的是GenericApplicationContext.refreshBeanFactory()方法,代碼如下:

protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException(
                "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
}

主要是通過getId()方法給BeanFactory設(shè)置serializationId的屬性值瓶殃,此處設(shè)置的值為:"org.springframework.context.annotation.AnnotationConfigApplicationContext@6cc7b4de"

2.2 獲取BeanFactory
public final ConfigurableListableBeanFactory getBeanFactory() {
    return this.beanFactory;
}

此處返回的是DefaultListableBeanFactory

3. 對(duì)BeanFactory進(jìn)行功能填充

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    beanFactory.setBeanClassLoader(getClassLoader());
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // Configure the bean factory with context callbacks.
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // Register early post-processor for detecting inner beans as ApplicationListeners.
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 增加對(duì)AspectJ的支持
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 添加默認(rèn)的系統(tǒng)環(huán)境Bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

此方法是對(duì)BeanFactory進(jìn)行一些設(shè)置充包。關(guān)于這一塊的解說可以參考容器的功能擴(kuò)展(二)功能擴(kuò)展

4. postProcessBeanFactory

postProcessBeanFactory方法是一個(gè)擴(kuò)展點(diǎn),在AbstractApplicationContext中是一個(gè)空方法遥椿,它主要是讓子類可以重寫以實(shí)現(xiàn)它們自己的特殊邏輯基矮。而AnnotationConfigApplicationContext并沒有對(duì)該類進(jìn)行重寫。

5.激活各種BeanFactoryPostProcessor處理器

BeanFactoryPostProcessorBeanPostProcessor都是Spring提供的擴(kuò)展點(diǎn)冠场,可以通過它們對(duì)bean的定義修改家浇。

 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
     PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

     // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
     // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
     if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
         beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
         beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
     }
 }

激活BeanFactoryPostProcessor的工作會(huì)委托給PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()進(jìn)行處理。

6. 注冊(cè)各種Bean的后置處理器

Bean的后置處理器的方法也是委托給PostProcessorRegistrationDelegate處理碴裙,具體方法為:registerBeanPostProcessors()钢悲。這?只是注冊(cè),真正的調(diào)?是在getBean的時(shí)候

待續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市舔株,隨后出現(xiàn)的幾起案子莺琳,更是在濱河造成了極大的恐慌,老刑警劉巖载慈,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惭等,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡办铡,警方通過查閱死者的電腦和手機(jī)辞做,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寡具,“玉大人秤茅,你說我怎么就攤上這事∩硅荆” “怎么了嫂伞?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我帖努,道長(zhǎng)撰豺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任拼余,我火速辦了婚禮污桦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匙监。我一直安慰自己凡橱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布亭姥。 她就那樣靜靜地躺著稼钩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪达罗。 梳的紋絲不亂的頭發(fā)上坝撑,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音粮揉,去河邊找鬼巡李。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扶认,可吹牛的內(nèi)容都是我干的侨拦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辐宾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼狱从!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起螃概,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤矫夯,失蹤者是張志新(化名)和其女友劉穎鸽疾,沒想到半個(gè)月后吊洼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡制肮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年冒窍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豺鼻。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡综液,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出儒飒,到底是詐尸還是另有隱情谬莹,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站附帽,受9級(jí)特大地震影響埠戳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蕉扮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一整胃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喳钟,春花似錦屁使、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至易茬,卻和暖如春共郭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疾呻。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工除嘹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岸蜗。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓尉咕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親璃岳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子年缎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring容器高層視圖 Spring 啟動(dòng)時(shí)讀取應(yīng)用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof閱讀 2,812評(píng)論 1 24
  • 1.1 spring IoC容器和beans的簡(jiǎn)介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器铃慷,...
    simoscode閱讀 6,713評(píng)論 2 22
  • 一.什么是Ioc /DI ioc容器:主要是完成了對(duì)象的創(chuàng)建和依賴的管理注入 二.Spring IoC體系結(jié)構(gòu) (...
    紅牛蜀黍閱讀 2,143評(píng)論 0 7
  • 這幾個(gè)月单芜,一直害眼疾,前前后后做了三次手術(shù)犁柜,可是老是頑固性的復(fù)發(fā)洲鸠,這不,沒有辦法馋缅,只好再去醫(yī)院扒腕,做檢查以前,例行驗(yàn)...
    健的XUYU閱讀 205評(píng)論 1 1
  • 樂觀組6號(hào) 姓名:李科龍 擔(dān)雪老師的話拯救了我 感悟:良師益友要多交萤悴,因?yàn)槟氵€活著瘾腰,活著就有煩惱,人必須朝前看覆履,堅(jiān)...
    李科龍閱讀 1,042評(píng)論 0 0