SpringBoot啟動(dòng)流程解析

寫在前面:

? ? ? ?由于該系統(tǒng)是底層系統(tǒng),以微服務(wù)形式對(duì)外暴露dubbo服務(wù),所以本流程中SpringBoot不基于jetty或者tomcat等容器啟動(dòng)方式發(fā)布服務(wù),而是以執(zhí)行程序方式啟動(dòng)來(lái)發(fā)布(參考下圖keepRunning方法)。

? ? ? ?本文以調(diào)試一個(gè)實(shí)際的SpringBoot啟動(dòng)程序?yàn)槔?,參考流程中主要類類圖,來(lái)分析其啟動(dòng)邏輯和自動(dòng)化配置原理。


總覽:

? ? ? ?上圖為SpringBoot啟動(dòng)結(jié)構(gòu)圖,我們發(fā)現(xiàn)啟動(dòng)流程主要分為三個(gè)部分,第一部分進(jìn)行SpringApplication的初始化模塊,配置一些基本的環(huán)境變量、資源、構(gòu)造器、監(jiān)聽(tīng)器,第二部分實(shí)現(xiàn)了應(yīng)用具體的啟動(dòng)方案,包括啟動(dòng)流程的監(jiān)聽(tīng)模塊、加載配置環(huán)境模塊、及核心的創(chuàng)建上下文環(huán)境模塊,第三部分是自動(dòng)化配置模塊,該模塊作為springboot自動(dòng)配置核心,在后面的分析中會(huì)詳細(xì)討論。在下面的啟動(dòng)程序中我們會(huì)串聯(lián)起結(jié)構(gòu)中的主要功能。

啟動(dòng):

? ? ? 每個(gè)SpringBoot程序都有一個(gè)主入口,也就是main方法,main里面調(diào)用SpringApplication.run()啟動(dòng)整個(gè)spring-boot程序,該方法所在類需要使用@SpringBootApplication注解,以及@ImportResource注解(if need),@SpringBootApplication包括三個(gè)注解,功能如下:@EnableAutoConfiguration:SpringBoot根據(jù)應(yīng)用所聲明的依賴來(lái)對(duì)Spring框架進(jìn)行自動(dòng)配置

@SpringBootConfiguration(內(nèi)部為@Configuration):被標(biāo)注的類等于在spring的XML配置文件中(applicationContext.xml),裝配所有bean事務(wù),提供了一個(gè)spring的上下文環(huán)境

@ComponentScan:組件掃描,可自動(dòng)發(fā)現(xiàn)和裝配Bean,默認(rèn)掃描SpringApplication的run方法里的Booter.class所在的包路徑下文件,所以最好將該啟動(dòng)類放到根包路徑下

SpringBoot啟動(dòng)類

首先進(jìn)入run方法

run方法中去創(chuàng)建了一個(gè)SpringApplication實(shí)例,在該構(gòu)造方法內(nèi),我們可以發(fā)現(xiàn)其調(diào)用了一個(gè)初始化的initialize方法

這里主要是為SpringApplication對(duì)象賦一些初值。構(gòu)造函數(shù)執(zhí)行完畢后,我們回到run方法

該方法中實(shí)現(xiàn)了如下幾個(gè)關(guān)鍵步驟:

1.創(chuàng)建了應(yīng)用的監(jiān)聽(tīng)器SpringApplicationRunListeners并開(kāi)始監(jiān)聽(tīng)

2.加載SpringBoot配置環(huán)境(ConfigurableEnvironment),如果是通過(guò)web容器發(fā)布,會(huì)加載StandardEnvironment,其最終也是繼承了ConfigurableEnvironment,類圖如下

可以看出,*Environment最終都實(shí)現(xiàn)了PropertyResolver接口,我們平時(shí)通過(guò)environment對(duì)象獲取配置文件中指定Key對(duì)應(yīng)的value方法時(shí),就是調(diào)用了propertyResolver接口的getProperty方法

3.配置環(huán)境(Environment)加入到監(jiān)聽(tīng)器對(duì)象中(SpringApplicationRunListeners)

4.創(chuàng)建run方法的返回對(duì)象:ConfigurableApplicationContext(應(yīng)用配置上下文),我們可以看一下創(chuàng)建方法:

方法會(huì)先獲取顯式設(shè)置的應(yīng)用上下文(applicationContextClass),如果不存在,再加載默認(rèn)的環(huán)境配置(通過(guò)是否是web environment判斷),默認(rèn)選擇AnnotationConfigApplicationContext注解上下文(通過(guò)掃描所有注解類來(lái)加載bean),最后通過(guò)BeanUtils實(shí)例化上下文對(duì)象,并返回,ConfigurableApplicationContext類圖如下:

主要看其繼承的兩個(gè)方向:

LifeCycle:生命周期類,定義了start啟動(dòng)、stop結(jié)束、isRunning是否運(yùn)行中等生命周期空值方法

ApplicationContext:應(yīng)用上下文類,其主要繼承了beanFactory(bean的工廠類)

5.回到run方法內(nèi),prepareContext方法將listeners、environment、applicationArguments、banner等重要組件與上下文對(duì)象關(guān)聯(lián)

6.接下來(lái)的refreshContext(context)方法(初始化方法如下)將是實(shí)現(xiàn)spring-boot-starter-*(mybatis、redis等)自動(dòng)化配置的關(guān)鍵,包括spring.factories的加載,bean的實(shí)例化等核心工作。

refresh方法

配置結(jié)束后,Springboot做了一些基本的收尾工作,返回了應(yīng)用環(huán)境上下文。回顧整體流程,Springboot的啟動(dòng),主要?jiǎng)?chuàng)建了配置環(huán)境(environment)、事件監(jiān)聽(tīng)(listeners)、應(yīng)用上下文(applicationContext),并基于以上條件,在容器中開(kāi)始實(shí)例化我們需要的Bean,至此,通過(guò)SpringBoot啟動(dòng)的程序已經(jīng)構(gòu)造完成,接下來(lái)我們來(lái)探討自動(dòng)化配置是如何實(shí)現(xiàn)。


自動(dòng)化配置:

? ? ? ?之前的啟動(dòng)結(jié)構(gòu)圖中,我們注意到無(wú)論是應(yīng)用初始化還是具體的執(zhí)行過(guò)程,都調(diào)用了SpringBoot自動(dòng)配置模塊

SpringBoot自動(dòng)配置模塊

? ? ? ?該配置模塊的主要使用到了SpringFactoriesLoader,即Spring工廠加載器,該對(duì)象提供了loadFactoryNames方法,入?yún)閒actoryClass和classLoader,即需要傳入上圖中的工廠類名稱和對(duì)應(yīng)的類加載器,方法會(huì)根據(jù)指定的classLoader,加載該類加器搜索路徑下的指定文件,即spring.factories文件,傳入的工廠類為接口,而文件中對(duì)應(yīng)的類則是接口的實(shí)現(xiàn)類,或最終作為實(shí)現(xiàn)類,所以文件中一般為如下圖這種一對(duì)多的類名集合,獲取到這些實(shí)現(xiàn)類的類名后,loadFactoryNames方法返回類名集合,方法調(diào)用方得到這些集合后,再通過(guò)反射獲取這些類的類對(duì)象、構(gòu)造方法,最終生成實(shí)例

工廠接口與其若干實(shí)現(xiàn)類接口名稱

下圖有助于我們形象理解自動(dòng)配置流程

SpringBoot自動(dòng)化配置關(guān)鍵組件關(guān)系圖?

? ? ? mybatis-spring-boot-starter、spring-boot-starter-web等組件的META-INF文件下均含有spring.factories文件,自動(dòng)配置模塊中,SpringFactoriesLoader收集到文件中的類全名并返回一個(gè)類全名的數(shù)組,返回的類全名通過(guò)反射被實(shí)例化,就形成了具體的工廠實(shí)例,工廠實(shí)例來(lái)生成組件具體需要的bean。

之前我們提到了EnableAutoConfiguration注解,其類圖如下

可以發(fā)現(xiàn)其最終實(shí)現(xiàn)了ImportSelector(選擇器)和BeanClassLoaderAware(bean類加載器中間件),重點(diǎn)關(guān)注一下AutoConfigurationImportSelector的selectImports方法

該方法在springboot啟動(dòng)流程——bean實(shí)例化前被執(zhí)行,返回要實(shí)例化的類信息列表。我們知道,如果獲取到類信息,spring自然可以通過(guò)類加載器將類加載到j(luò)vm中,現(xiàn)在我們已經(jīng)通過(guò)spring-boot的starter依賴方式依賴了我們需要的組件,那么這些組建的類信息在select方法中也是可以被獲取到的,不要急我們繼續(xù)向下分析

該方法中的getCandidateConfigurations方法,通過(guò)方法注釋了解到,其返回一個(gè)自動(dòng)配置類的類名列表,方法調(diào)用了loadFactoryNames方法,查看該方法

在上面的代碼可以看到自動(dòng)配置器會(huì)跟根據(jù)傳入的factoryClass.getName()到項(xiàng)目系統(tǒng)路徑下所有的spring.factories文件中找到相應(yīng)的key,從而加載里面的類。我們就選取這個(gè)mybatis-spring-boot-autoconfigure下的spring.factories文件

進(jìn)入org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中,主要看一下類頭

發(fā)現(xiàn)@Spring的Configuration,儼然是一個(gè)通過(guò)注解標(biāo)注的springBean,繼續(xù)向下看,

@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class})這個(gè)注解的意思是:當(dāng)存在SqlSessionFactory.class, SqlSessionFactoryBean.class這兩個(gè)類時(shí)才解析MybatisAutoConfiguration配置類,否則不解析這一個(gè)配置類,make sence,我們需要mybatis為我們返回會(huì)話對(duì)象,就必須有會(huì)話工廠相關(guān)類

@CondtionalOnBean(DataSource.class):只有處理已經(jīng)被聲明為bean的dataSource

@ConditionalOnMissingBean(MapperFactoryBean.class)這個(gè)注解的意思是如果容器中不存在name指定的bean則創(chuàng)建bean注入,否則不執(zhí)行(該類源碼較長(zhǎng),篇幅限制不全粘貼)

? ? ? ?以上配置可以保證sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的組件均可被自動(dòng)配置,@Configuration注解已經(jīng)提供了Spring的上下文環(huán)境,所以以上組件的配置方式與Spring啟動(dòng)時(shí)通過(guò)mybatis.xml文件進(jìn)行配置起到一個(gè)效果。通過(guò)分析我們可以發(fā)現(xiàn),只要一個(gè)基于SpringBoot項(xiàng)目的類路徑下存在SqlSessionFactory.class, SqlSessionFactoryBean.class,并且容器中已經(jīng)注冊(cè)了dataSourceBean,就可以觸發(fā)自動(dòng)化配置,意思說(shuō)我們只要在maven的項(xiàng)目中加入了mybatis所需要的若干依賴,就可以觸發(fā)自動(dòng)配置,但引入mybatis原生依賴的話,每集成一個(gè)功能都要去修改其自動(dòng)化配置類,那就得不到開(kāi)箱即用的效果了。所以Spring-boot為我們提供了統(tǒng)一的starter可以直接配置好相關(guān)的類,觸發(fā)自動(dòng)配置所需的依賴(mybatis)如下:

這里是截取的mybatis-spring-boot-starter的源碼中pom.xml文件中所有依賴:

因?yàn)閙aven依賴的傳遞性,我們只要依賴starter就可以依賴到所有需要自動(dòng)配置的類,實(shí)現(xiàn)開(kāi)箱即用的功能。也體現(xiàn)出Springboot簡(jiǎn)化了Spring框架帶來(lái)的大量XML配置以及復(fù)雜的依賴管理,讓開(kāi)發(fā)人員可以更加關(guān)注業(yè)務(wù)邏輯的開(kāi)發(fā)。

最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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