spring-boot源碼分析之beanFactory · 伍

前言
原本是打算昨天把prepareContext方法梳理完,今天開(kāi)始啃硬骨頭——refresh方法的,結(jié)果大家昨天就知道了:由于篇幅的問(wèn)題,剩余內(nèi)容放在今天分享了。
然后今天的想法是除了昨天需要補(bǔ)充的內(nèi)容外,看能不能把refresh的內(nèi)容也分享一部分,畢竟是硬骨頭,然而現(xiàn)實(shí)情況是,在我把昨天需要補(bǔ)充的內(nèi)容梳理之后,發(fā)現(xiàn)剩余的內(nèi)容還不少,所以就只能先不開(kāi)始新的內(nèi)容了,等prepareContext方法徹底剖析完畢之后我們?cè)倏杏补穷^。
下面就讓我們看下prepareContext的剩余內(nèi)容吧!
prepareContext補(bǔ)充
下面這些內(nèi)容原本是昨天要一起分享的,但是由于內(nèi)容比較多,而且昨天時(shí)間有點(diǎn)晚了,所以就沒(méi)來(lái)得及分享,下面我們就先來(lái)做一些簡(jiǎn)單的補(bǔ)充說(shuō)明。
創(chuàng)建BeanDefinitionLoader

首先是創(chuàng)建bean的定義加載器,創(chuàng)建beanDefiniton加載器的時(shí)候,首先是一個(gè)賦值操作,然后創(chuàng)建了AnnotatedBeanDefinitionReader,它的創(chuàng)建就稍微有點(diǎn)復(fù)雜。
實(shí)例化ConditionEvaluator
創(chuàng)建AnnotatedBeanDefinitionReader的時(shí)候要先創(chuàng)建ConditionEvaluator,而ConditionEvaluator實(shí)例化的時(shí)候又需要先實(shí)例化ConditionContextImpl,這里的ConditionEvaluator和ConditionContextImpl是spring boot引入的新特性,主要用于條件配置,我們可以通過(guò)@Conditional注解使用這一特性,后面專(zhuān)門(mén)來(lái)分析吧(又發(fā)現(xiàn)新大陸了……)。
最后一步都是賦值操作,就沒(méi)啥好講的了。

另外,在load方法中調(diào)用了另一個(gè)方法registerAnnotationConfigProcessors。就說(shuō)這方法看著眼熟,原來(lái)我們昨天分析beanDefinitionMap初始化的時(shí)候,已經(jīng)研究過(guò)了。 
這里注冊(cè)完成后beanDefinitionMap會(huì)多出5個(gè)元素。 
下面兩個(gè)Reader的初始化就不再詳細(xì)分析了,基本上都是簡(jiǎn)單的實(shí)例化和賦值操作: 
實(shí)例化scanner
后面兩個(gè)操作有必要講一下,首先實(shí)例化了scanner,在實(shí)例化過(guò)程中,注冊(cè)了三個(gè)注解類(lèi)型過(guò)濾器(AnnotationTypeFilter)

事實(shí)上,javax.inject.Named,并沒(méi)有被注冊(cè)成功,因?yàn)楫?dāng)前class并不存在,關(guān)于這一點(diǎn),下面的截圖可以很清楚地說(shuō)明,因?yàn)樵谧?cè)JSR-330的class時(shí)候報(bào)了java.lang.ClassNotFoundException異常,所以最終的結(jié)果就是@Component和@ManagedBean這兩種注解類(lèi)型攔截器被成功注入,我們通過(guò)includeFilters的大小也可以看出這一點(diǎn)。

注冊(cè)排除過(guò)濾器
排除過(guò)濾器(excludeFilters)就是過(guò)濾不需要被掃描的資源。所以這一步的操作就比較簡(jiǎn)單了,就是往scanner的 排除過(guò)濾器(excludeFilters)中增加了一個(gè)ClassExcludeFilter,傳入的是sources,目前sources只有我們spring boot項(xiàng)目的主類(lèi)。
這里需要注意的是,excludeFilters本身是個(gè)List,所以這里add的時(shí)候其實(shí)是把新的排除注冊(cè)器放在excludeFilters的最前面。

設(shè)置loader屬性
下面就是這只loader的屬性,包括beanNameGennerator、resourceLoader和environment。但是由于這些參數(shù)都是空的,所以默認(rèn)情況下這些設(shè)置方法并不會(huì)被執(zhí)行:

loader加載資源
下面是load方法最核心的執(zhí)行流程,核心部分就是注冊(cè)Bean

由于一張截圖無(wú)法完整體現(xiàn)registerBean的流程,所以我分成了兩張截圖,這里最核心的就是doRegisterBean方法,這個(gè)方法內(nèi)部會(huì)構(gòu)建當(dāng)前class的BeanDefinition。其中AnnotatedGenericBeanDefinition是根據(jù)注解信息生成的bean的definition信息,包括beanClass、AnnotationMetadata(注解元數(shù)據(jù))、scope(范圍)等,總之就是凡是通過(guò)注解方式配置的類(lèi),都是通過(guò)AnnotatedGenericBeanDefinition來(lái)構(gòu)建它的definition信息的,它本身也就是為了支持注解元數(shù)據(jù)而生的。

上面這些操作執(zhí)行完畢后會(huì)往beanFactory的beanDefinitionMap和beanDefinitionNames分別保存beanClass的beanName和beanDefinition信息
執(zhí)行事件監(jiān)聽(tīng)
這里調(diào)用的是監(jiān)聽(tīng)器容器的contextLoaded方法,其方法內(nèi)部本質(zhì)上是觸發(fā)對(duì)應(yīng)的事件,從方法名上我們可以看出來(lái)這個(gè)方法其實(shí)發(fā)的就是容器已經(jīng)加載完成的事件。下面是Logger

debug過(guò)程中,我發(fā)現(xiàn)這里觸發(fā)的時(shí)間類(lèi)型是ApplicationPeparedEvent事件,這里的事件類(lèi)別挺多的,不同的監(jiān)聽(tīng)器會(huì)執(zhí)行各自的onApplicationEvent方法,上面的流程中我們就是以LoggingApplicationListener為例,剖析其執(zhí)行流程的

對(duì)于LoggingApplicationListener總共會(huì)處理5種事件,這里處理的是ApplicationPreparedEvent事件,也就是我們前面看到的類(lèi)型

在當(dāng)前事件類(lèi)型下,LoggingApplicationListener的onApplicationEvent方法會(huì)往容器中注冊(cè)兩個(gè)單例實(shí)例,中間的logFile為空,所以并未成功注入。

總結(jié)
好了,到今天我們算是把prepareContext方法的內(nèi)容梳理完了,整體來(lái)看還算清晰。
說(shuō)實(shí)話(huà),我是沒(méi)想到一小塊補(bǔ)充內(nèi)容能有這么多,但事實(shí)確實(shí)如此,這也從側(cè)面說(shuō)明了spring boot本身確實(shí)比較復(fù)雜,任何一小行代碼,其內(nèi)部可能都潛藏著核心的實(shí)現(xiàn)代碼。我數(shù)了一下,不算括號(hào)和if語(yǔ)句,只有17行,但是就這短短的17行代碼,我用了近三千字還沒(méi)有說(shuō)的特別清楚,我自己都不敢想象,不過(guò)好的一點(diǎn)是,雖然中間磕磕絆絆,但也算是啃下來(lái)了,不過(guò)更硬的骨頭還在后頭——refresh方法才是重頭戲,還是不能松懈呀~
最后,我想說(shuō)的是,任何一小行代碼很有可能就是解開(kāi)你心中困惑的鑰匙,所以不論是坑,還是意外收獲,更多時(shí)候都需要我們自己親自蹚~
- END -