看透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的依賴關系,沒法調用Bean的初始化方法。在我個人看來,這個論述不完全對。因為其實依賴關系我們是可以通過從Class中的FieldMethodConstructor中的注解(@Autowired @Resource)來獲取到的,當然初始化方法的注解(@PostConstruct等)也可以獲取到,所以就這兩點來看單純使用Class對象是可以描述Bean的。但是有3點Class對象是做不到或則會很難做好的。

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

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


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

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

如果你還是不理解這個比喻的話,也沒關系,你只需要記得,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的初始化前后來定制自己的邏輯,所以你翻開源代碼的時候是看到兩個方法,一個是初始化之前調用,一個初始化之后調用。在這里我們暫時不討論啥叫初始化,后面我們會聊。那不理解的話,我們也打個比方,在魚出鍋之前你是不是可以放點醬油好提鮮上色,出鍋后你是不是可以再放點香菜提香。

總結(非常非常重要):

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

Spring的內(nèi)衣

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

注意,前方高能,下面要看一點源代碼了。當然我們看源代碼的目的不是去看細節(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ā)者手動調用addBeanFactoryPostProcessor這個方法才會有值。而正常我們在使用Spring的時候是不會手動調用這個方法的。

  • 這里其實有一個問題:如果有多個BeanFactoryPostProcessor或者BeanDefinitionRegistryPostProcessor的話,那么誰先被調用誰又后被調用呢?
    在內(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注解驅動模式下的一個開天辟地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所遵循的設計原則:

開閉原則:對修改關閉,對擴展開放

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才能修改啊,還有一個原因:如果要調用開發(fā)者自定義的BeanFactoryPostProcessor,那么在容器初期是需要容器自己來發(fā)現(xiàn)BeanFactoryPostProcessor,然后再實例化這些BeanFactoryPostProcessor的,那怎么發(fā)現(xiàn)呢?得先經(jīng)過女媧娘娘ConfigurationClassPostProcessor的掃描才能發(fā)現(xiàn)開發(fā)者自定義的BeanFactoryPostProcessor,然后再調用BeanFactoryPostProcessor

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

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

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

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

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

容器啟動階段,主要是兩個巨牛逼的接口在發(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注冊到容器中,正常情況下是不會調用BeanPostProcessor的任何方法的。

注意,這只是正常情況下是不會調用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.

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

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

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

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

裝配屬性值

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

初始化

  1. 調用aware接口回調方法 invokeAwareMethods:包括 BeanNameAwareBeanClassLoaderAwareBeanFactoryAware

  2. 調用初始化之前的回調方法: BeanPostProcessorpostProcessBeforeInitialization

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

  4. 初始化之后的回調方法 BeanPostProcessorpostProcessAfterInitialization

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

Constructor > @PostConstruct > InitializingBean > init-method

Constructor 首先被調用很容易理解,首先實例化,然后再初始化。而InitializingBean的afterPropertiesSet方法在init-method之前調用也比較容易理解,因為源代碼就是這么先后調用的嗎。那為啥@PostConstruct會在InitializingBean 的afterPropertiesSet方法之前被調用呢?其實,很簡單,因為@PostConstruct的之所以起作用,是通過CommonAnnotationBeanPostProcessorpostProcessBeforeInitialization方法實現(xiàn)的。而前面的生命周期中說得很明白postProcessBeforeInitialization方法是在初始化之前被調用的。所以嚴格來說@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)容,敬請期待。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

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