spring-boot源碼分析之BeanFactory · 陸

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

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

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

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

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

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

在方法內(nèi)部,首先通過getEnvironment方法獲取系統(tǒng)的環(huán)境設置,然后同獲取到的環(huá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。
關于這里獲取到的ConfigurableWebEnvironment,我們做一點點擴展補充,ConfigurableWebEnvironment主要的屬性是profiles和資源解析器,這里的defaultProfiles表示spring boot的默認配置文件,activeProfiles表示spring boot啟動時激活的配置文件,從下面截圖也可以很清楚看出來:

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

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

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

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

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

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