寫在前面:
? ? ? ?由于該系統(tǒng)是底層系統(tǒng),以微服務(wù)形式對外暴露dubbo服務(wù)礁鲁,所以本流程中SpringBoot不基于jetty或者tomcat等容器啟動方式發(fā)布服務(wù)八酒,而是以執(zhí)行程序方式啟動來發(fā)布(參考下圖keepRunning方法)。
? ? ? ?本文以調(diào)試一個實際的SpringBoot啟動程序為例,參考流程中主要類類圖鳍悠,來分析其啟動邏輯和自動化配置原理。
總覽:
? ? ? ?上圖為SpringBoot啟動結(jié)構(gòu)圖,我們發(fā)現(xiàn)啟動流程主要分為三個部分监署,第一部分進行SpringApplication的初始化模塊,配置一些基本的環(huán)境變量纽哥、資源钠乏、構(gòu)造器、監(jiān)聽器昵仅,第二部分實現(xiàn)了應(yīng)用具體的啟動方案缓熟,包括啟動流程的監(jiān)聽模塊、加載配置環(huán)境模塊摔笤、及核心的創(chuàng)建上下文環(huán)境模塊够滑,第三部分是自動化配置模塊,該模塊作為springboot自動配置核心吕世,在后面的分析中會詳細討論彰触。在下面的啟動程序中我們會串聯(lián)起結(jié)構(gòu)中的主要功能。
啟動:
? ? ? 每個SpringBoot程序都有一個主入口命辖,也就是main方法况毅,main里面調(diào)用SpringApplication.run()啟動整個spring-boot程序,該方法所在類需要使用@SpringBootApplication注解尔艇,以及@ImportResource注解(if need)尔许,@SpringBootApplication包括三個注解,功能如下:@EnableAutoConfiguration:SpringBoot根據(jù)應(yīng)用所聲明的依賴來對Spring框架進行自動配置
@SpringBootConfiguration(內(nèi)部為@Configuration):被標注的類等于在spring的XML配置文件中(applicationContext.xml)终娃,裝配所有bean事務(wù)味廊,提供了一個spring的上下文環(huán)境
@ComponentScan:組件掃描,可自動發(fā)現(xiàn)和裝配Bean棠耕,默認掃描SpringApplication的run方法里的Booter.class所在的包路徑下文件余佛,所以最好將該啟動類放到根包路徑下
首先進入run方法
run方法中去創(chuàng)建了一個SpringApplication實例,在該構(gòu)造方法內(nèi)窍荧,我們可以發(fā)現(xiàn)其調(diào)用了一個初始化的initialize方法
這里主要是為SpringApplication對象賦一些初值辉巡。構(gòu)造函數(shù)執(zhí)行完畢后,我們回到run方法
該方法中實現(xiàn)了如下幾個關(guān)鍵步驟:
1.創(chuàng)建了應(yīng)用的監(jiān)聽器SpringApplicationRunListeners并開始監(jiān)聽
2.加載SpringBoot配置環(huán)境(ConfigurableEnvironment)蕊退,如果是通過web容器發(fā)布郊楣,會加載StandardEnvironment憔恳,其最終也是繼承了ConfigurableEnvironment,類圖如下
可以看出痢甘,*Environment最終都實現(xiàn)了PropertyResolver接口喇嘱,我們平時通過environment對象獲取配置文件中指定Key對應(yīng)的value方法時,就是調(diào)用了propertyResolver接口的getProperty方法
3.配置環(huán)境(Environment)加入到監(jiān)聽器對象中(SpringApplicationRunListeners)
4.創(chuàng)建run方法的返回對象:ConfigurableApplicationContext(應(yīng)用配置上下文)塞栅,我們可以看一下創(chuàng)建方法:
方法會先獲取顯式設(shè)置的應(yīng)用上下文(applicationContextClass)者铜,如果不存在,再加載默認的環(huán)境配置(通過是否是web environment判斷)放椰,默認選擇AnnotationConfigApplicationContext注解上下文(通過掃描所有注解類來加載bean)作烟,最后通過BeanUtils實例化上下文對象,并返回砾医,ConfigurableApplicationContext類圖如下:
主要看其繼承的兩個方向:
LifeCycle:生命周期類拿撩,定義了start啟動、stop結(jié)束如蚜、isRunning是否運行中等生命周期空值方法
ApplicationContext:應(yīng)用上下文類压恒,其主要繼承了beanFactory(bean的工廠類)
5.回到run方法內(nèi),prepareContext方法將listeners错邦、environment探赫、applicationArguments、banner等重要組件與上下文對象關(guān)聯(lián)
6.接下來的refreshContext(context)方法(初始化方法如下)將是實現(xiàn)spring-boot-starter-*(mybatis撬呢、redis等)自動化配置的關(guān)鍵伦吠,包括spring.factories的加載,bean的實例化等核心工作魂拦。
配置結(jié)束后毛仪,Springboot做了一些基本的收尾工作,返回了應(yīng)用環(huán)境上下文芯勘∠溲ィ回顧整體流程,Springboot的啟動荷愕,主要創(chuàng)建了配置環(huán)境(environment)衡怀、事件監(jiān)聽(listeners)、應(yīng)用上下文(applicationContext)路翻,并基于以上條件,在容器中開始實例化我們需要的Bean茄靠,至此茂契,通過SpringBoot啟動的程序已經(jīng)構(gòu)造完成,接下來我們來探討自動化配置是如何實現(xiàn)慨绳。
自動化配置:
? ? ? ?之前的啟動結(jié)構(gòu)圖中掉冶,我們注意到無論是應(yīng)用初始化還是具體的執(zhí)行過程真竖,都調(diào)用了SpringBoot自動配置模塊
? ? ? ?該配置模塊的主要使用到了SpringFactoriesLoader,即Spring工廠加載器厌小,該對象提供了loadFactoryNames方法恢共,入?yún)閒actoryClass和classLoader,即需要傳入上圖中的工廠類名稱和對應(yīng)的類加載器璧亚,方法會根據(jù)指定的classLoader讨韭,加載該類加器搜索路徑下的指定文件,即spring.factories文件癣蟋,傳入的工廠類為接口透硝,而文件中對應(yīng)的類則是接口的實現(xiàn)類,或最終作為實現(xiàn)類疯搅,所以文件中一般為如下圖這種一對多的類名集合濒生,獲取到這些實現(xiàn)類的類名后,loadFactoryNames方法返回類名集合幔欧,方法調(diào)用方得到這些集合后罪治,再通過反射獲取這些類的類對象、構(gòu)造方法礁蔗,最終生成實例
下圖有助于我們形象理解自動配置流程
? ? ? mybatis-spring-boot-starter觉义、spring-boot-starter-web等組件的META-INF文件下均含有spring.factories文件,自動配置模塊中瘦麸,SpringFactoriesLoader收集到文件中的類全名并返回一個類全名的數(shù)組谁撼,返回的類全名通過反射被實例化,就形成了具體的工廠實例滋饲,工廠實例來生成組件具體需要的bean厉碟。
之前我們提到了EnableAutoConfiguration注解,其類圖如下
可以發(fā)現(xiàn)其最終實現(xiàn)了ImportSelector(選擇器)和BeanClassLoaderAware(bean類加載器中間件)屠缭,重點關(guān)注一下AutoConfigurationImportSelector的selectImports方法
該方法在springboot啟動流程——bean實例化前被執(zhí)行箍鼓,返回要實例化的類信息列表。我們知道呵曹,如果獲取到類信息款咖,spring自然可以通過類加載器將類加載到j(luò)vm中,現(xiàn)在我們已經(jīng)通過spring-boot的starter依賴方式依賴了我們需要的組件奄喂,那么這些組建的類信息在select方法中也是可以被獲取到的铐殃,不要急我們繼續(xù)向下分析
該方法中的getCandidateConfigurations方法,通過方法注釋了解到跨新,其返回一個自動配置類的類名列表富腊,方法調(diào)用了loadFactoryNames方法,查看該方法
在上面的代碼可以看到自動配置器會跟根據(jù)傳入的factoryClass.getName()到項目系統(tǒng)路徑下所有的spring.factories文件中找到相應(yīng)的key域帐,從而加載里面的類赘被。我們就選取這個mybatis-spring-boot-autoconfigure下的spring.factories文件
進入org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中是整,主要看一下類頭
發(fā)現(xiàn)@Spring的Configuration,儼然是一個通過注解標注的springBean民假,繼續(xù)向下看浮入,
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class})這個注解的意思是:當存在SqlSessionFactory.class, SqlSessionFactoryBean.class這兩個類時才解析MybatisAutoConfiguration配置類,否則不解析這一個配置類,make sence羊异,我們需要mybatis為我們返回會話對象事秀,就必須有會話工廠相關(guān)類
@CondtionalOnBean(DataSource.class):只有處理已經(jīng)被聲明為bean的dataSource
@ConditionalOnMissingBean(MapperFactoryBean.class)這個注解的意思是如果容器中不存在name指定的bean則創(chuàng)建bean注入,否則不執(zhí)行(該類源碼較長球化,篇幅限制不全粘貼)
? ? ? ?以上配置可以保證sqlSessionFactory秽晚、sqlSessionTemplate、dataSource等mybatis所需的組件均可被自動配置筒愚,@Configuration注解已經(jīng)提供了Spring的上下文環(huán)境赴蝇,所以以上組件的配置方式與Spring啟動時通過mybatis.xml文件進行配置起到一個效果。通過分析我們可以發(fā)現(xiàn)巢掺,只要一個基于SpringBoot項目的類路徑下存在SqlSessionFactory.class, SqlSessionFactoryBean.class句伶,并且容器中已經(jīng)注冊了dataSourceBean,就可以觸發(fā)自動化配置陆淀,意思說我們只要在maven的項目中加入了mybatis所需要的若干依賴考余,就可以觸發(fā)自動配置,但引入mybatis原生依賴的話轧苫,每集成一個功能都要去修改其自動化配置類楚堤,那就得不到開箱即用的效果了。所以Spring-boot為我們提供了統(tǒng)一的starter可以直接配置好相關(guān)的類含懊,觸發(fā)自動配置所需的依賴(mybatis)如下:
這里是截取的mybatis-spring-boot-starter的源碼中pom.xml文件中所有依賴:
因為maven依賴的傳遞性身冬,我們只要依賴starter就可以依賴到所有需要自動配置的類,實現(xiàn)開箱即用的功能岔乔。也體現(xiàn)出Springboot簡化了Spring框架帶來的大量XML配置以及復雜的依賴管理酥筝,讓開發(fā)人員可以更加關(guān)注業(yè)務(wù)邏輯的開發(fā)。