Spring Boot是如何調(diào)用到initializeBean()的?

寫在最前

《Spring是如何回調(diào)Aware系列接口?》這篇文章的最后留了個(gè)小尾巴,下面我們就圍繞著這個(gè)“小尾巴”進(jìn)行分析,來說說initializeBean()是如何被調(diào)用到的。

正文

《Spring Boot創(chuàng)建Beans的過程分析》的最后說道:beans的創(chuàng)建是AbstractAutowireCapableBeanFactory類的createBean(String beanName, RootBeanDefinition mbd, Object[] args)方法中,如下圖:

AbstractAutowireCapableBeanFactory

那現(xiàn)在,我們接著上次的文章分析,分析下 - Spring Boot是如何調(diào)用到initializeBean()方法的?

還記得initializeBean()方法做些什么嗎?簡單回顧下:

initializeBean()

initializeBean()中主要做的是初始化了部分beans、應(yīng)用了spring的beans生命周期中的一些回調(diào)方法。
如:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。。。詳細(xì)的內(nèi)容請(qǐng)看《Spring是如何回調(diào)Aware系列接口?》這篇文章。

1. AbstractAutowireCapableBeanFactory的createBean(xxx) 方法
AbstractAutowireCapableBeanFactory的createBean(xxx)方法

從上圖中我們看出,在調(diào)用createBean(xxx)方法的過程中,獲取到了beanName,mdbToUse(BeanDefinition)和args三個(gè)參數(shù),這個(gè)三個(gè)參數(shù)很關(guān)鍵。beanName定義了該bean的名字,通常我們使用Resource和Qualifier注解的時(shí)候會(huì)用到;mdbToUse是一個(gè)BeanDefinition,也就是我們通常在xml文件中定義的<bean xxx/>元素的對(duì)象表示;args是spring框架的xml解析器解析到的傳入?yún)?shù),用來調(diào)用構(gòu)造函數(shù)或工廠方法來創(chuàng)建Bean對(duì)象的。然后,調(diào)用了doCreateBean()方法。

2. AbstractAutowireCapableBeanFactory的doCreateBean(xxx) 方法
doCreateBean(xxx)方法

看上圖中的注釋,也可以說明beanName,mdbToUse和args這三個(gè)參數(shù)的含義。這個(gè)方法比較長,其中有一行關(guān)鍵的代碼是我們要討論的:


doCreateBean(xxx)

看到這里,終于發(fā)現(xiàn)我們的initializeBean()方法被調(diào)用了。下面我們?cè)敿?xì)分析下initializeBean()方法里面到底做了些什么.

initializeBean()

我們以圖中的序列號(hào)為單位,進(jìn)行逐一分析:
1.invokeAwareMethods()方法

invokeAwareMethods()方法

此方法的入?yún)?shù)是String 和Object類型。為什么這里要設(shè)置成Object類型的呢?因?yàn)槲覀兊腷ean對(duì)象可能實(shí)現(xiàn)了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware中的任意幾個(gè),為了兼容所有類型,方便向下轉(zhuǎn)型,這里就用Object了。該方法以回調(diào)的方式調(diào)用Aware接口的實(shí)現(xiàn),把spring內(nèi)部的對(duì)象傳遞到外部供開發(fā)者使用,同時(shí)也支持了用戶以回調(diào)的方式自定義了spring內(nèi)部的一些對(duì)象或配置。

2.applyBeanPostProcessorsBeforeInitialization()方法

applyBeanPostProcessorsBeforeInitialization()

此方法獲取了所有的BeanPostProcessor對(duì)象。然后,循環(huán)調(diào)用了所有BeanPostProcessor的postProcessBeforeInitialization方法。這里我們就不說BeanPostProcessor是如何被設(shè)置到Spring里的了。我們看看官方是如何定義BeanPostProcessor的:Factory hook that allows for custom modification of new bean instances,也就是說你可以在這里實(shí)現(xiàn)初始化bean前的一些自定義。從此方法的簽名,我們也可以看出來,此方法會(huì)在bean初始化前調(diào)用。什么?。?!等等,難道這時(shí)候bean還沒有被初始化???很奇怪是吧,我們先留下這個(gè)問題,稍后再討論。

3.invokeInitMethods()方法

invokeInitMethods()方法

從上圖的注釋中,我們得知:invokeInitMethods方法是給了bean一個(gè)使用設(shè)置好的properties的機(jī)會(huì),也給了一個(gè)了解自己所在的beanFactory的機(jī)會(huì),同時(shí)檢查該bean有沒有實(shí)現(xiàn)InitializingBean接口或者是在xml配置文件中自定義了bean的init-method方法,以便回調(diào)它們。
從上面的話,我們可以推斷出:該bean已經(jīng)被前面的某個(gè)步驟給實(shí)例化了,并且設(shè)置好了它的properties,這樣我們才可以使用這些properties做些事情。這樣也就解釋了,前面的疑問 - bean還沒被初始化???其實(shí),bean已經(jīng)被實(shí)例化, 這里的初始化是指調(diào)用InitializingBean的afterPropertiesSet()或開發(fā)者自定義的init-method,并不等同于實(shí)例化,請(qǐng)不要混淆了!至于什么時(shí)候?qū)嵗驮O(shè)置properties的,我們以后在分析。

if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {

上面的這行代碼有點(diǎn)晦澀難懂,意思是:
a. 必須實(shí)現(xiàn)了InitializingBean接口,否則退出這個(gè)if語句。
b. 實(shí)現(xiàn)了InitializingBean接口還不行,還必須mbd == null或者mbd不為null時(shí)但必須不包含afterPropertiesSet() 方法。

我覺得我說了等于沒說!那這個(gè)到底是什么意思呢?好了,我不賣關(guān)子了。既然,spring是在初始化bean,那你想:bean會(huì)被多次初始化嗎?肯定不是的吧,在整個(gè)生命周期過程中只應(yīng)該被初始化一次。mbd是個(gè)BeanDefinition對(duì)象,對(duì)應(yīng)著xml配置文件中<bean xxx>元素的定義。如果該bean的實(shí)例已經(jīng)存在了,那么mbd將會(huì)為空??催@個(gè)方法上的注釋也是這么說明的:can also be null, if given an existing bean instance。

bean已經(jīng)存在了,mbd就會(huì)為空?那這是為什么呢?我自己是這樣解釋:通常我們獲取bean的時(shí)候,是通過getBean方法。當(dāng)bean不存在時(shí),spring需要從已經(jīng)解析過的眾多BeanDefinition中找到我們需要的,然后設(shè)置給mbd;如果在根據(jù)beanName獲取bean的時(shí)候,發(fā)現(xiàn)該bean已經(jīng)存在了,那么mbd就不要再次獲取了,所以就為空了(猜想,需要證明)。

那既然實(shí)例已經(jīng)bean存在了,直接調(diào)用afterPropertiesSet()方法,也就沒什么問題了。那如果mbd !=null也就是說,bean是第一次初始化,如果發(fā)現(xiàn)該bean對(duì)應(yīng)的BeanDefinition對(duì)象mbd中也包含了一個(gè)afterPropertiesSet方法,那么就不執(zhí)行if語句塊了。

String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd); // 通過反射調(diào)用
}

而這段代碼,是spring通過反射調(diào)用了init-method方式設(shè)置的方法,調(diào)用的時(shí)機(jī)是mbd!=null,也就是第一次初始化的時(shí)候。具體代碼我就不說了,比較簡單。

4.applyBeanPostProcessorsAfterInitialization()方法

applyBeanPostProcessorsAfterInitialization()方法

這個(gè)方法和applyBeanPostProcessorsBeforeInitialization()方法類似,就沒什么好說的了。

寫在最后

分析過程中,可選擇的路徑可能比較多,我只挑選了其中的一條分析路徑。
就寫到這里吧,該下班了!晚安!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,273評(píng)論 6 342
  • Spring容器高層視圖 Spring 啟動(dòng)時(shí)讀取應(yīng)用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof閱讀 2,917評(píng)論 1 24
  • 女兒,當(dāng)本科的錄取通知書送達(dá)我們家的時(shí)候,我和你媽媽的心情無比激動(dòng),因?yàn)槟阋殉晒Φ赝瓿闪税謰尩男脑福蔀橐幻?..
    東江之子閱讀 1,478評(píng)論 6 8
  • 今天是馬克讀書訓(xùn)練營第四天,七月四號(hào),發(fā)神經(jīng)一般的天氣,完美詮釋了陰晴不定,大起大落。前一篇讀書感我才說到作為紅色...
    蘭浥塵閱讀 348評(píng)論 0 0
  • 十月間隙 那天下了一整天的雨 上山途中沿路而行,有山,有水,有溪流,有小瀑布,還有路邊的野梨,樹上的板栗,還有一排...
    我不愛起名字閱讀 2,392評(píng)論 0 0

友情鏈接更多精彩內(nèi)容