看透Spring

哪有什么歲月靜好指蚁,只是有人在默默負重前行

1. Spring的復雜

如果你是一個Javaer,那么你一定使用過Spring勃痴。我們一邊在感嘆Spring的無比強大盔憨,然而一邊在嘆息Spring強大的外表下隱藏著那如迷失森林搬的迷宮式的復雜。


迷宮

這還不是最令人絕望的缨称,最為絕望的是當你想看本書或者看篇博客來試圖了解Spring為啥這么強大的時候凝果,往往一開始都是這樣的畫風

或者是這樣的畫風


然后你就強忍著睡意,眼神迷離地看完了(或者將翻完了)那本
<<Spring xxx內(nèi)功秘籍>>睦尽,然后假裝自己已經(jīng)練會了Spring的內(nèi)功器净。。当凡。

2. 俯視Spring

當然我不是說不需要深入到Spring的源代碼山害,而是說你不要一上來就整這些感覺很高大上然后又讓人看得很懵逼的圖。

那應該咋整呢沿量?
答:你得循序漸進浪慌,從大到小,一步一步慢慢深入,我把這種學習方式叫做 剝洋蔥式學習欧瘪,或者粗俗點來講叫做扒衣式學習

下面我們就來看一下Spring的全貌:


沒錯眷射,這是Spring-framwork官網(wǎng)上有且僅有的一張描述Spring的圖。

是不是感覺很清爽佛掖,很舒服妖碉,其實這才是我們學習Spring應該要有的姿勢:別廢話,有圖有真相芥被。

從這張圖中我們看到普通用戶只要準備兩樣東西就可以使用我們的Spring了

  • Pojo
  • 配置信息

這也是我們大多數(shù)開發(fā)者眼中的Spring----強大的Spring,萬能的Spring欧宜。所有一切Spring的黑魔法都被隱藏在了Spring容器中。

剖析Spring

下面我們就來一層層趴開Spring華麗的外衣拴魄,來瞅瞅Spring的黑魔法到底是如何生效的

Spring的外衣

上面我們遠遠地俯視了一下Spring,看了個大概冗茸,下面我們就再扒一層Spring的衣服來稍微深入瞅一下Spring。


所有的黑魔法匹中,其實不管它有怎么華麗的展現(xiàn)方式夏漱,其內(nèi)部的實現(xiàn)步驟其實也是一步一步來的。如上圖顶捷,其實Spring的啟動過程分為兩個階段:

  1. 容器啟動階段(為了得到BeanDefinition
  2. Bean實例化階段(為了得到裝配好的Bean

BeanDefinition非常非常重要)插播(插播內(nèi)容都可以先不看

  • BeanDefinition到底是個啥挂绰?

對比來看,這個就有點像是Class之于Object,是用來描述Bean的元數(shù)據(jù)的服赎。比如(Bean是否是單例的葵蒂,是否需要延遲加載交播,初始化方法名稱是啥,依賴哪些Bean)践付。

  • 有人可能會問Spring為啥不直接使用Class對象來生產(chǎn)Bean呢秦士?

有人可能會說單純使用Class的話,沒法完整的描述Spring中的所有概念:比如沒法描述Bean的依賴關(guān)系隧土,沒法調(diào)用Bean的初始化方法。在我個人看來命爬,這個論述不完全對。因為其實依賴關(guān)系我們是可以通過從Class中的Field遇骑,Method揖曾,Constructor中的注解(@Autowired @Resource)來獲取到的,當然初始化方法的注解(@PostConstruct等)也可以獲取到炭剪,所以就這兩點來看單純使用Class對象是可以描述Bean的。但是有3點Class對象是做不到或則會很難做好的奴拦。

  1. Spring早期(XML時代)是沒有注解的媒鼓,Bean的依賴關(guān)系只能通過XML的配置來描述
  2. BeanDefinition是一個中間產(chǎn)物错妖,Spring是允許自己活著開發(fā)者來修改這個中間產(chǎn)物的,而Class是無法修改的
  3. 在注解時代暂氯,如果單純使用Class中的注解來描述一切Bean的元數(shù)據(jù)的話,Spring需要大量的反射操作才能拿到這些元數(shù)據(jù)痴施,這樣不僅性能低下而且非常繁瑣
  • 總的來說,你讓Class來描述Bean的話,Class估計只能說:臣妾辦不到啊G夼埂!神得!

如果你覺得這個過程有點突兀厘惦,或者有點懵逼,下面我們來打個比方:


這就好像你去飯店吃飯一樣循头,你只要坐那等上十幾分鐘然后你就可以享受到美味绵估。然而只要你真正地在家自己做過一頓飯炎疆,你就知道這個過程是多么漫長和繁雜。但是這一切飯店的后廚都幫你搞定了国裳,你要做的就是享受美味形入。但是漫長繁雜的過程在廚師看來無非就是兩步:

  1. 準備材料(為了得到已經(jīng)洗好,處理好的的菜的原材料)
  2. 做菜(為了得到直接可以吃的菜)

如果你還是不理解這個比喻的話缝左,也沒關(guān)系亿遂,你只需要記得,Spring容器加載在整體上分為兩個部分:

  • 1.器啟動階段(為了得到BeanDefinition)
  • 2.Bean實例化階段(為了得到裝配好的Bean)
    大體記得就行渺杉,至于每一部分里面具體都干了些啥事蛇数,請繼續(xù)往下看

Spring的秋衣,秋褲

上面我們看到了Spring的華麗的外衣(兩階段)是越,下面我們繼續(xù)耳舅,趴開外皮,看一下Spring的秋衣倚评,秋褲浦徊,看一下這個兩階段里面到底藏了些啥。

老規(guī)矩天梧,先上圖


從上圖中我們可以看到

  1. 第一階段(容器啟動階段)主要使用了BeanFactoryPostProcessor來從XML配置文件或者Java代碼中掃描得到生產(chǎn)Bean的原材料:BeanDefinition

  2. 第二階段(Bean實例化階段)主要使用了第一步得到的BeanDefinition以及BeanPostProcessor來生產(chǎn)Bean

OK盔性,至此為止,我們知道了實際干活的就是這兩個角色

  • BeanFactoryPostProcessor(有些中文翻譯為:Bean工廠后置處理器)
  • BeanPostProcessor(有些中文翻譯為:Bean后置處理器)

那這兩位到底是哪路神仙呢呢岗?

BeanFactoryPostProcessor非常非常重要

BeanFactoryPostProcessor在Spring中是一個接口冕香,然后它還有一個非常重要的子接BeanDefinitionRegistryPostProcessor。簡單理解其作用:BeanFactoryPostProcessor允許框架或者開發(fā)者來修改BeanDefinition后豫,而BeanDefinitionRegistryPostProcessor允許框架或者開發(fā)者來向容器中動態(tài)添加更多的BeanDefinition悉尾。不理解的話,我們打個比方:BeanFactoryPostProcessor 的作用就相當于菜洗好了之后硬贯,你還可以再切成段或者切成片再或者加一點胡椒粉焕襟,也就是你可以修改原材料的形態(tài)鸵赖。BeanDefinitionRegistryPostProcessor的作用就像是菜洗好了放在菜籃子后它褪,你自己還可以再添加自己喜歡的原材料茫打,比如再加點生羊肉老赤,牛肉等。

BeanPostProcessor非常非常重要

BeanPostProcessor是一個接口簇汉柒,總共有5個碾褂,這里我就先不一一列舉出來了正塌,但是你得知道我們上圖中所畫的BeanPostProcessor不僅僅指BeanPostProcessor本身,還代指其下的所有子接口逮壁。但是這些接口其實都是用來生產(chǎn)裝配好的Bean的窥淆。BeanPostProcessor本身的主要作用是在Bean的初始化前后來定制自己的邏輯忧饭,所以你翻開源代碼的時候是看到兩個方法词裤,一個是初始化之前調(diào)用逆航,一個初始化之后調(diào)用。在這里我們暫時不討論啥叫初始化抹剩,后面我們會聊胡嘿。那不理解的話灶平,我們也打個比方,在魚出鍋之前你是不是可以放點醬油好提鮮上色瞒爬,出鍋后你是不是可以再放點香菜提香侧但。

總結(jié)(非常非常重要):

上圖中的BeanFactoryPostProcessor以及BeanPostProcessor都不僅僅指它兩本身禀横,而代指的是接口簇。BeanFactoryPostProcessor干預的是Bean的原材料BeanDefinition的生產(chǎn)過程趾娃,而BeanPostProcessor干預的是Bean的生產(chǎn)過程

Spring的內(nèi)衣

上面我們已經(jīng)趴開了Spring的外套和秋衣,秋褲了笤成,下面我們就要羞羞了疹启,嘿嘿喊崖,來瞅一下Spring的內(nèi)衣茁裙。

注意,前方高能廊宪,下面要看一點源代碼了箭启。當然我們看源代碼的目的不是去看細節(jié)放妈,而是來證明我們的理解是對的,然后再在看完源代碼后得出一些結(jié)論宅倒。如果你懶得看源代碼唉堪,那么你就忽略然后直接看結(jié)論吧,一點也不影響你的理解持痰。

下面的Spring源代碼的是基于5.1.9-RELEASE版本前酿,而且只講基于注解的Spring加載流程

稍微看過Spring的源代碼的同學都知道罢维,Spring最為核心的方法就是那個AbstractApplicationContext#refresh()方法匀借。

該方法定義了抽象了Spring容器加載的整個流程吓肋。只不過有些方法是空實現(xiàn)肤舞,是留給子類Override的。你要是學習過設計模式杖爽,其實這就是模板模式慰安。其實Spring源代碼中有很多模式化焕,我們碰到了就提一下,沒碰到就算了

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        try {
            postProcessBeanFactory(beanFactory);
            // 第一階段(容器準備階段):從上面第一個方法到這個方法為止都算是第一階段
            // 但是最為重要的流程主要就是這個方法
            invokeBeanFactoryPostProcessors(beanFactory);

            // 過度階段:注冊BeanPostProcessor(包含其所有的子接口)
            registerBeanPostProcessors(beanFactory);

            // 下面的四個方法先忽略:非IOC容器的主要流程
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();

            // 第二階段(Bean實例化階段):實例化生效的非延遲加載的Bean
            finishBeanFactoryInitialization(beanFactory);

            finishRefresh();
        }
        catch (BeansException ex) {
            // 忽略,非主流程
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        }
        finally {
            resetCommonCaches();// 忽略谜疤,非主流程
        }
    }
}

看不懂夷磕?不要緊坐桩,你只要大概讀一讀注釋就可以了,主要是為了驗證我們之前的Spring的二階段流程在源代碼中的體現(xiàn)萍鲸。下面我們就盡量通過畫圖的方式來深入了解一下Spring脊阴。

第一階段(容器啟動階段)剖析

第一階段的精華都在這方法里面了嘿期,具體的代碼這里就不貼了,大家有興趣可以自己翻看源代碼蜜猾。

AbstractApplicationContext.invokeBeanFactoryPostProcessors(beanFactory);

再升入一層,發(fā)現(xiàn)這個過程被委托給了PostProcessorRegistrationDelegate

PostProcessorRegistrationDelegate#
    public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

這里要注意一下肩豁,正常情況下參數(shù)beanFactoryPostProcessors都是空的清钥,因為這個參數(shù)只有在開發(fā)者手動調(diào)用addBeanFactoryPostProcessor這個方法才會有值祟昭。而正常我們在使用Spring的時候是不會手動調(diào)用這個方法的础钠。

  • 這里其實有一個問題:如果有多個BeanFactoryPostProcessor或者BeanDefinitionRegistryPostProcessor的話停局,那么誰先被調(diào)用誰又后被調(diào)用呢企孩?
    在內(nèi)部Spring把對象主要分為3類:
  1. 實現(xiàn)了PriorityOrdered(優(yōu)先級最高)接口的Bean
  2. 實現(xiàn)了Ordered(一般優(yōu)先級)接口的Bean
  3. 都沒有實現(xiàn)PriorityOrderedOrdered的普通Bean(優(yōu)先級最低)

然后在同級別(如都實現(xiàn)了PriorityOrdered的Bean)的Bean中通過實現(xiàn)getOrder()方法來區(qū)分誰先誰后

如上圖就是整個第一階段(容器啟動階段)最為核心的一段流程了补疑。
你可能會說,這尼瑪也太簡單了吧锹杈,你是不是在誤導我寥假?其實,Spring的主流程就是這么簡單粪滤,但是Spring之所以這么復雜是因為,在這個過程中Spring加入了它自己實現(xiàn)的BeanDefinitionRegistryPostProcessor以及BeanFactoryPostProcessor。如果你看過Spring的實現(xiàn)扫腺,你就知道Spring就是一頭隱藏的很深的惡魔。

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor在容器準備階段的主要作用就是注冊所有需要加載到容器中的Bean的元數(shù)據(jù)BeanDefinition迫吐。

下面我們就簡單介紹一個Spring注解驅(qū)動模式下的一個開天辟地BeanDefinitionRegistryPostProcessor接口的實現(xiàn)類---ConfigurationClassPostProcessor闪萄,這個類是Spring-framwork中唯一實現(xiàn)了BeanDefinitionRegistryPostProcessor接口的類

如果非要我形容一下ConfigurationClassPostProcessor的重要性的話,那么只能使用女媧娘娘來形容了。對的赊时,ConfigurationClassPostProcessor之于開發(fā)者的Pojo業(yè)務類舟奠,就想女媧娘娘之于人類抬纸。

上帝說膜蛔,世上還沒有人類墅茉,于是女媧娘娘來了战转。Spring說,容器中還沒有Bean,于是ConfigurationClassPostProcessor來了。

那這個類ConfigurationClassPostProcessor主要是干嘛的呢膀懈?
我們來看一下ConfigurationClassPostProcessor的文檔:

BeanFactoryPostProcessor used for bootstrapping processing of @Configuration classes.

說白點就是處理所有的配置類

其內(nèi)部的實現(xiàn)刘陶,今天就不在這里討論了疑苫。你主要記住它很重要很重要碰逸,其作用就是處理配置類。

到這里湃番,你是不是有點迷糊了,我感覺Spring只需要一個ConfigurationClassPostProcessor就夠了啊弄屡,為啥還要搞那么復雜,又是PriorityOrdered又是Ordered的,整這些有啥用呢壶笼?
實際上,這里不得不提一下Spring所遵循的設計原則:

開閉原則:對修改關(guān)閉,對擴展開放

Spring之所以搞這么復雜鹦筹,主要是為了方便Spring框架自己以及開發(fā)者來擴展Spring练对。比如Spring不僅僅可以支持注解來配置虚青,還可以支持XML方式下隧,那么Spring是不是可以實現(xiàn)一個BeanDefinitionRegistryPostProcessor來處理XML方式呢?(當然到了Spring4.x的時候支救,這個功能已經(jīng)被@ImportResource注解來代替了)。那除了Spring自己可以實現(xiàn),開發(fā)者是不是也可以實現(xiàn)一個
BeanDefinitionRegistryPostProcessor來擴展Spring呢?

不明白嗎引几?那我打個比方:廚師把魚洗好叽掘,切好盖腕,處理好之后放到了菜籃子里面了膛薛。但是這時候你說你想吃個雞腿怎么辦呢?那簡單,只要你自己去菜場買個雞腿椿访,然后自己洗好,處理好之后也放到菜籃子里面就可以了猪腕,這時候廚師就不僅可以把魚做好彻采,而且可以給你做好雞腿岭粤。


PS:是不是已經(jīng)留哈喇子了

BeanFactoryPostProcessor

說完了雞腿虎囚,啊,不,說完了BeanDefinitionRegistryPostProcessor之后嫉嘀,我們來聊一下BeanFactoryPostProcessor

BeanFactoryPostProcessor的主要作用就是來通過修改容器中已經(jīng)存在的BeanDefinition來插手Bean的產(chǎn)生過程洛退。說白點,就是來搞事情的驼仪。

所以,你現(xiàn)在是不是可以理解上面那個圖中為啥先處理BeanDefinitionRegistryPostProcessor然后再處理BeanFactoryPostProcessor了吧?得先有BeanDefinition才能修改啊柔滔,還有一個原因:如果要調(diào)用開發(fā)者自定義的BeanFactoryPostProcessor,那么在容器初期是需要容器自己來發(fā)現(xiàn)BeanFactoryPostProcessor,然后再實例化這些BeanFactoryPostProcessor的,那怎么發(fā)現(xiàn)呢绒净?得先經(jīng)過女媧娘娘ConfigurationClassPostProcessor的掃描才能發(fā)現(xiàn)開發(fā)者自定義的BeanFactoryPostProcessor下翎,然后再調(diào)用BeanFactoryPostProcessor胆萧。

那具體我們可以對BeanDefinition修改些啥呢虏辫?

比如羹唠,在Spring中主要的實現(xiàn)類是PropertyPlaceholderConfigurer,主要是為了替換Bean屬性中的${}變量為真正的屬性值验辞。當然其實在注解驅(qū)動的年代族购,這個類已經(jīng)沒那么重要了违施。這個類主要在XML時代,處理Bean屬性中的${}變量,如下

    <bean id="hello" class="com.example.bean.User">
        <property name="name" value="${name}"></property>
    </bean>

如果你問還有沒有其他的應用站削,目前來看,在注解驅(qū)動的年代,我還真沒有想出其他的應用場景,歡迎各位有實際應用場景的給我留言伦乔。

第一階段(容器啟動階段)總結(jié)

容器啟動階段,主要是兩個巨牛逼的接口在發(fā)揮作用招刹。由BeanDefinitionRegistryPostProcessor接口的實現(xiàn)類來實現(xiàn)對所有Bean的掃描從而得到BeanDefinition训柴,然后再由BeanFactoryPostProcessor接口的實現(xiàn)類來對BeanDefinition進行修改完善越锈。至此Bean的原材料就已經(jīng)準備好了

過度階段(注冊BeanPostProcessor)

注意:這個階段在很多資料上都沒有,這只是我自己定義的一個階段德撬,不過這都不重要,你只要知道我說的這個階段它做了啥事蝠咆,然后產(chǎn)生啥作用就可以了再芋。

這個階段的入口方法是

registerBeanPostProcessors(beanFactory);

你會發(fā)現(xiàn)直接委托給了這個方法

PostProcessorRegistrationDelegate#registerBeanPostProcessors

好了记某,這里只是為了記錄一下入口,好方便你可以以后查看源代碼喘帚,我們這里就不直接分析源代碼了。

在我們完成第一階段之后倾鲫,我們的容器中就已經(jīng)有了所有Bean的元數(shù)據(jù)BeanDefinition以及已經(jīng)實例化好的BeanFactoryPostProcessor。接下來其實我們就要拿著BeanDefinition來實例化Bean了。但是在實例化的時候還缺少及其重要的輔材BeanPostProcessor捅厂。

所以這個過度階段的主要作用就是向容器中注冊實例化好并且排好序的BeanPostProcessor贿堰。

上圖就是已經(jīng)排好序的所有的BeanPostProcessor。你會發(fā)現(xiàn)這跟第一階段BeanFactoryPostProcessor的排序規(guī)則很像。但是要注意的是,這里只是把排好序的BeanPostProcessor注冊到容器中趣效,正常情況下是不會調(diào)用BeanPostProcessor的任何方法的干花。

注意,這只是正常情況下是不會調(diào)用BeanPostProcessor的任何方法的。但是有一種情況比較特殊馏锡,比如BeanPostProcessor依賴了普通Bean,那么就會導致實例化低優(yōu)先級的BeanPostProcessor的時候伟端,由于BeanPostProcessor依賴了普通Bean杯道,那么就會先去實例化普通Bean后注入到實例化好的BeanPostProcessor中。這種情況會導致我們的普通Bean提前初始化责蝠,從而導致普通Bean沒有被所有的BeanPostProcessor處理過,有可能缺失一部分功能(如Spring的異步署海,或者沒有被自己定義的BeanPostProcessor處理)丰辣。具體可以參考我前面的文章
Spring Bug深度歷險記
Spring擴展點(BeanPostProssor)之深度診斷歷險記

其實很多Spring的很魔法都要注意這些黑魔法背后的原理是是啥统屈,是用什么組件處理的窿侈,處理Spring容器加載的哪一個階段衙傀。如果前面階段的黑魔法依賴后面階段的黑魔法就會有問題金麸。這個我們到后面會將具體的場景喜最。

你要說這些BeanPostProcessor有啥用,我們這里暫且不談。你只要知道這是個過度階段哨免,為實例化Bean準備了很多有序的BeanPostProcessor

第二階段:實例化Bean

好了,終于到實例化Bean階段了笨忌。前面所有的準備都做好了,下面正式進入主題:生產(chǎn)Bean。也就是最令人激動的后廚開始要炒菜了。

在講具體的生產(chǎn)Bean的過程之前,我們先要明確生產(chǎn)Bean有兩個必不可少的原材料

  • BeanDefinition
  • BeanPostProcessor接口簇(BeanPostProcessor及其子接口)

BeanPostProcessor接口簇其實有5個接口總共10個擴展方法尔许,全程參與了Bean的生命周期憔恳。Spring各種神奇的黑魔法基本上都是通過擴展實現(xiàn)BeanPostProcessor接口或者其子接口來實現(xiàn)的。比如@Autowired @Value注解是通過AutowiredAnnotationBeanPostProcessor來是實現(xiàn)的伦吠。還有大名鼎鼎的Aop功能是通過AbstractAutoProxyCreator來實現(xiàn)的茂契。

所以,其實Spring的一切黑魔法包括Aop其實都是通過擴展實現(xiàn)Spring預留的擴展接口來實現(xiàn)的礁蔗。因此何暮,Ioc容器才是Spring的根本事秀,而Aop只是Ioc容器擴展點之一楚堤。但是,不是說Aop不重要,而是說一切的黑魔法的根本其實就是Spring IOC容器的各種擴展點的花式應用看铆。

下面記錄一下源代碼的位置

AbstractApplicationContext{
   refresh(){
       // .....
      finishBeanFactoryInitialization()
   }
   finishBeanFactoryInitialization(){
      //...
      // Instantiate all remaining (non-lazy-init) singletons.
     beanFactory.preInstantiateSingletons();
   }
}

DefaultListableBeanFactory{
    preInstantiateSingletons(){}
}
AbstractBeanFactory{
    getBean()
}

好了岩臣,下面我們就來分析一下Spring是如何創(chuàng)建Bean的


生產(chǎn)bean的流程其實就是這么幾步项鬼。其中最為核心的也就是前三步:

  1. 實例化Bean
  2. 裝配屬性值
  3. 初始化

實例化Bean

你可能會說不就new個對象嗎丈牢,怎么就能整這么多幺蛾子呢庙曙?這個嗎也好理解,因為它是Spring嗎,它做啥都是對的廊移,而且還感覺這么做很牛逼。

魯迅曾經(jīng)說過:我家門前有兩顆樹,一顆是棗樹,另外一顆也是棗樹危喉。

你品崩哩,你細細品猩系。

好了沪铭,我們繼續(xù)扯Spring。其實Spring在實例化Bean階段是做了很多事的。

  1. 首先第一步凤藏,Spring在實例化具體的對象之前奸忽,給了BeanPostProcessor一次機會,來返回代理對象而不是實際的目標對象揖庄。

Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.

具體點就是你可以調(diào)用InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation方法來返回一個你自己想要的對象栗菜,如果返回的不是null,那么只會調(diào)用BeanPostProcessorpostProcessAfterInitialization方法抠艾。之后該Bean就不會再執(zhí)行初始化方法苛萎,依賴注入等生命周期了。也就是說這是一個短路回調(diào)方法检号。那這有個啥作用呢?目前最主要的作用就是在AOP中的應用蛙酪,AOP的基類AbstractAutoProxyCreator實現(xiàn)了該方法。這里只需要有一個影響就可以了。

  1. 接下來指厌,要是短路沒成功的話凉逛,那么就需要真正實例化了。但是實例化又沒有那么簡單,因為Spring中可以使用構(gòu)造方法來做依賴注入玛痊,那么在實例化對象的時候就要決定使用哪一個構(gòu)造方法來實例化了汰瘫。這個過程交給了SmartInstantiationAwareBeanPostProcessordetermineCandidateConstructors方法。

  2. 然后實例化之后還會再回調(diào)MergedBeanDefinitionPostProcessorpostProcessMergedBeanDefinition方法 來進一步有限度地修改BeanDefinition或者預處理一些注解(如預處理class擂煞,并緩存加了@Autowired注解的element)

  3. 最后為了處理循環(huán)應用混弥,還調(diào)用了SmartInstantiationAwareBeanPostProcessorgetEarlyBeanReference方法來提前暴露引用。

裝配屬性值

  1. 首先調(diào)用InstantiationAwareBeanPostProcessorpostProcessAfterInstantiation方法來決定是否有必要裝備屬性值对省。
  2. 如果有必要(正常情況下都是有必要的)蝗拿,那么就需要執(zhí)行InstantiationAwareBeanPostProcessorpostProcessProperties或者postProcessPropertyValues來處理依賴裝配。

初始化

  1. 調(diào)用aware接口回調(diào)方法 invokeAwareMethods:包括 BeanNameAware 蒿涎,BeanClassLoaderAware 哀托,BeanFactoryAware

  2. 調(diào)用初始化之前的回調(diào)方法: BeanPostProcessorpostProcessBeforeInitialization

  3. 調(diào)用初始化方法(invokeInitMethods)
    InitializingBeanafterPropertiesSet
    自定義初始化方法(initMethod)

  4. 初始化之后的回調(diào)方法 BeanPostProcessorpostProcessAfterInitialization

這個時候你也許就明白了,關(guān)于初始化的幾個方法的先后順序了

Constructor > @PostConstruct > InitializingBean > init-method

Constructor 首先被調(diào)用很容易理解劳秋,首先實例化仓手,然后再初始化。而InitializingBean的afterPropertiesSet方法在init-method之前調(diào)用也比較容易理解玻淑,因為源代碼就是這么先后調(diào)用的嗎嗽冒。那為啥@PostConstruct會在InitializingBean 的afterPropertiesSet方法之前被調(diào)用呢?其實岁忘,很簡單辛慰,因為@PostConstruct的之所以起作用,是通過CommonAnnotationBeanPostProcessorpostProcessBeforeInitialization方法實現(xiàn)的干像。而前面的生命周期中說得很明白postProcessBeforeInitialization方法是在初始化之前被調(diào)用的帅腌。所以嚴格來說@PostConstruct并不是初始化方法,而是初始化之前的方法麻汰。不過速客,不重要,反正初始化和初始化之前也沒有其他事情了五鲫,所以也可以近似認為@PostConstruct也是一種初始化方法吧溺职。

至此,我們已經(jīng)把Bean生命周期中最重要的前3部分已經(jīng)講清楚了位喂。文章到這已經(jīng)顯得比較長了浪耘,但是還是有很多內(nèi)容沒有將清楚。
1.各種BeanPostProcessor的具體實現(xiàn)類有哪些塑崖,都有些啥作用
2.Spring的生命周期中的各種擴展點有哪些七冲,怎么運用
3.AOP的實現(xiàn)原理
4.如何從Spring過渡到SpringBoot

后面我會一一講解這些內(nèi)容,敬請期待规婆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末澜躺,一起剝皮案震驚了整個濱河市蝉稳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掘鄙,老刑警劉巖耘戚,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異操漠,居然都是意外死亡收津,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門颅夺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來朋截,“玉大人,你說我怎么就攤上這事吧黄〔糠” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵拗慨,是天一觀的道長廓八。 經(jīng)常有香客問我,道長赵抢,這世上最難降的妖魔是什么剧蹂? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮烦却,結(jié)果婚禮上宠叼,老公的妹妹穿的比我還像新娘。我一直安慰自己其爵,他們只是感情好冒冬,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著摩渺,像睡著了一般简烤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上摇幻,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天横侦,我揣著相機與錄音,去河邊找鬼绰姻。 笑死枉侧,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的狂芋。 我是一名探鬼主播棵逊,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼银酗!你這毒婦竟也來了辆影?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤黍特,失蹤者是張志新(化名)和其女友劉穎蛙讥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灭衷,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡次慢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了翔曲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迫像。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瞳遍,靈堂內(nèi)的尸體忽然破棺而出闻妓,到底是詐尸還是另有隱情,我是刑警寧澤掠械,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布由缆,位于F島的核電站,受9級特大地震影響猾蒂,放射性物質(zhì)發(fā)生泄漏均唉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一肚菠、第九天 我趴在偏房一處隱蔽的房頂上張望舔箭。 院中可真熱鬧,春花似錦蚊逢、人聲如沸层扶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怒医。三九已至,卻和暖如春奢讨,著一層夾襖步出監(jiān)牢的瞬間稚叹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工拿诸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扒袖,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓亩码,卻偏偏與公主長得像季率,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子描沟,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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