spring-boot源碼分析之BeanFactory · 陸

前言
從今天開始,我們要啃硬骨頭了——refreshContext。這個(gè)方法就是我們一直在說的spring boot最核心的方法,這個(gè)方法執(zhí)行完成后,spring boot基本上就初始化完了,剩下的方法就是推送啟動(dòng)事件,回調(diào)相關(guān)的監(jiān)聽器之類的,反正就是吃透refresh方法,基本上就可以宣告革命勝利了,換句話說,就是如果我們啃下這塊硬骨頭之后,spring boot啟動(dòng)這塊我們就算徹底剖析完了,剩下的都是查漏補(bǔ)缺的小知識(shí)點(diǎn)了,摟草打兔子,順手的事。
好了,大話就說這么多,下面我們開始干活~
refreshContext
容器的刷新首先是從refreshContext方法開始的,然后refreshContext方法內(nèi)部會(huì)調(diào)用容器的refresh方法。
這里 refreshContext就做了兩件事,一個(gè)是注冊(cè)容器關(guān)閉鉤子函數(shù),另外一個(gè)就是刷新容器。
關(guān)閉鉤子函數(shù)可以讓我們更優(yōu)雅地關(guān)閉spring boot容器,這一塊后期也專門分享一次;刷新容器方法最終會(huì)調(diào)用容器的刷新方法,關(guān)于這個(gè)方法,我們之前已經(jīng)分享過了,t它的作用就是刷新容器中的持久化資源,這里的資源包括xml、java配置、注解、配置文件、數(shù)據(jù)庫等。

下面,我們就來詳細(xì)看下它的內(nèi)部實(shí)現(xiàn)。我們先看下它的調(diào)用流程:

從上面圖中我們可以看出來,最終其實(shí)調(diào)用的是AbstractApplicationContext的refresh方法,這個(gè)方法內(nèi)部比較長(zhǎng),總共調(diào)用了15個(gè)方法,下面我們就逐一剖析這些方法,不過工作日時(shí)間有限,今天可能也分享不了太多內(nèi)容,具體視情況而定吧。

prepareRefresh
我們先說prepareRefresh方法,這個(gè)方法的作用就是為后面的刷新操作做準(zhǔn)備,內(nèi)部實(shí)現(xiàn)如下:

簡(jiǎn)單解釋下它的執(zhí)行流程:
首先設(shè)置
spring boot的啟動(dòng)時(shí)間,獲取的是當(dāng)前時(shí)間;然后分別設(shè)置
closed和active為flase和true,這兩個(gè)屬性都是原子類AtomicBoolean。接著會(huì)根據(jù)日志設(shè)置等級(jí)輸出日志,不過這里必須是
begug級(jí)別才會(huì)輸出,如果是trace等級(jí)的,則會(huì)輸出更詳細(xì)的日志信息。再然后,它會(huì)調(diào)用
initPropertySources方法,這個(gè)方法的作用就是進(jìn)行配置資源初始化初始化,我們等下詳細(xì)剖析;再下面就是資源的校驗(yàn),這塊也需要展開分析
最后是監(jiān)聽器和監(jiān)聽事件的賦值操作
initPropertySources
initPropertySources方法進(jìn)行的就是property資源的初始化,當(dāng)前容器并沒有重寫該方法:

由于 AbstractApplicationContext的initPropertySources 方法是空實(shí)現(xiàn),而且AnnotationConfigServletWebServerApplicationContext并沒有重寫該方法,所以最后調(diào)用的是父類GenericWebApplicationContext的這個(gè)方法,父類的方法中最后調(diào)用的是ConfigurableWebEnvironment的配置資源初始化方法:

在方法內(nèi)部,首先通過getEnvironment方法獲取系統(tǒng)的環(huán)境設(shè)置,然后同獲取到的環(huán)境設(shè)置進(jìn)行配置資源初始化,其中servletConfig的入?yún)⒅苯訛?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">null。
關(guān)于這里獲取到的ConfigurableWebEnvironment,我們做一點(diǎn)點(diǎn)擴(kuò)展補(bǔ)充,ConfigurableWebEnvironment主要的屬性是profiles和資源解析器,這里的defaultProfiles表示spring boot的默認(rèn)配置文件,activeProfiles表示spring boot啟動(dòng)時(shí)激活的配置文件,從下面截圖也可以很清楚看出來:

關(guān)于profile文件,相比各位小伙伴應(yīng)該都不陌生,在spring boot中我們的配置文件就叫profile,默認(rèn)情況下的配置文件是application.*,文件類型可以是properties文件或者yaml文件,我們通常通過spring.profiles.active=@profileActive@(``yml`方式類似)指定需要激活的文件(環(huán)境配置)

獲取完配置資源之后,會(huì)調(diào)用ConfigurableWebEnvironment的initPropertySources方法,下面是initPropertySources方法的內(nèi)部調(diào)用流程。在debug過程中,我發(fā)現(xiàn)默認(rèn)情況下servletContext和servletConfig都為空,所以replace方法實(shí)際并未執(zhí)行。

validateRequiredProperties
這里校驗(yàn)是根據(jù)我們AbstractPropertyResolver解析器的requiredProperties屬性進(jìn)行判斷的,如果存在為空的配置就會(huì)報(bào)錯(cuò),說這個(gè)方法內(nèi)部實(shí)現(xiàn)也很簡(jiǎn)單,流程也不是很復(fù)雜,就是一些簡(jiǎn)單的資源獲取和空判斷:

這里的requiredProperties和environment有關(guān),它是AbstractEnvironment的setRequiredProperties方法中初始化的,關(guān)于這個(gè)方法的調(diào)用,等我們回頭分析environment初始化的時(shí)候再來研究。

監(jiān)聽器賦值
prepareRefresh0方法的最后就是一些監(jiān)聽器集合的賦值操作,earlyApplicationListeners表示預(yù)刷新容器應(yīng)用監(jiān)聽器集合(pre-refresh ApplicationListeners),applicationListeners表示當(dāng)前容器監(jiān)聽器集合,earlyApplicationEvents就表示預(yù)刷新應(yīng)用監(jiān)聽事件。

其實(shí)也就是監(jiān)聽器的初始化,代碼也很簡(jiǎn)單,加上注釋就很容器理解,我也就不再贅述了。
總結(jié)
今天效率有點(diǎn)低呀,一整天就搞定了一個(gè)方法,確實(shí)有些離譜,但是也沒辦法,今天事情確實(shí)比較多——今天我負(fù)責(zé)處理oncall問題,然后下午又開了三個(gè)多小時(shí)的會(huì),下班那會(huì)還在排查處理線上問題,有點(diǎn)難呀~
不過,好在之前已經(jīng)把今天的內(nèi)容寫的七七八八了,不然今天新內(nèi)容就懸了……剛剛又看了下剩余的內(nèi)容,明天應(yīng)該可以搞定3個(gè)方法,然后還剩11個(gè)方法,后天4個(gè),大后天4個(gè),周一安排3個(gè),完美,所以今天就先到這里吧~
