文章目錄
- 1.概述
- 2.自動裝配原理分析
- 2.1@SpringBootConfiguration
- 2.2@EnableAutoConfiguration
- 3.自定義啟動器
1.概述
SpringBoot的自動裝配,減少了原先需要撰寫xml組態檔或java配置類,本文將分析SpringBoot自動裝配的原理(版本是2.3.5),從注解的作用到原始碼分析,再到自定義SpringBoot啟動器,
2.自動裝配原理分析
自動裝配的實作和這個注解有密切的關聯,該注解是個復合注解,里面包含了三個關鍵的注解
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

本文先介紹@SpringBootConfiguration和@EnableAutoConfiguration兩個注解,@ComponentScan注解放到下一篇博客,
重點分析@EnableAutoConfiguration注解的作用和實作原理,
2.1@SpringBootConfiguration
看到@Configuration注解表明該類是Spring的一個配置類
proxyBeanMethods = true 保證每個@Bean方法被呼叫多少次回傳的組件都是單實體的
proxyBeanMethods = false 每個@Bean方法被呼叫多少次回傳的組件都是新創建的

測驗代碼
-
proxyBeanMethods = true 默認就是true

控制臺輸出的結果如下:
獲取兩次物件都是同一個, -
proxyBeanMethods = false

控制臺輸出的結果如下:
此時兩個物件是不同的,
2.2@EnableAutoConfiguration
開啟自動裝配功能的注解

exclude和excludeName 指定自動裝配的類,不進行自動裝配,
例如下面的測驗代碼:
@SpringBootApplication()
public class Boot02Application {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Boot02Application.class, args);
MybatisAutoConfiguration bean = run.getBean(MybatisAutoConfiguration.class);
System.out.println(bean);
}
}
沒有設定exclude或者excludeName的時候,Spring容器當中存在MybatisAutoConfiguration物件,

將測驗代碼改成如下:
@SpringBootApplication(exclude = MybatisAutoConfiguration.class)
public class Boot02Application {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Boot02Application.class, args);
MybatisAutoConfiguration bean = run.getBean(MybatisAutoConfiguration.class);
System.out.println(bean);
}
}
// 或者 注意這里要寫全限定類名
@SpringBootApplication(excludeName= "org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration")
public class Boot02Application {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Boot02Application.class, args);
MybatisAutoConfiguration bean = run.getBean(MybatisAutoConfiguration.class);
System.out.println(bean);
}
}
此時出現例外,MybatisAutoConfiguration不存在Spring容器當中

除此之外該注解其中最主要的兩個注解是
@AutoConfigurationPackage 、@Import(AutoConfigurationImportSelector.class)
-
@AutoConfigurationPackage
添加該注解的類所在的package 作為自動配置package 進行管理,
所以SpringBoot啟動類所在的package作為自動配置的package,
配合@Import(AutoConfigurationPackages.Registrar.class)注解,將Registrar注入,Registrar實作了ImportBeanDefinitionRegistrar介面,該介面中的方法registerBeanDefinitions,用于Bean定義的注入,也就是這里將自動配置的package交給Spring管理,

-
@Import(AutoConfigurationImportSelector.class)
該注解在《Spring中@Import注解的使用和實作原理》博客中已經詳細介紹過了,用于Bean的注入,

AutoConfigurationImportSelector實作了DeferredImportSelector介面,該介面繼承自ImportSelector介面,我想當然的以為會執行selectImports方法,但是根據debug斷點除錯,并沒有執行該方法,
這是為什么呢?在ConfigurationClassParser的processImports方法中有這樣一段代碼,如下:

當實作類,實作的介面是DeferredImportSelector的時候,執行if的邏輯,如果它只是實作了父介面ImportSelector,才會執行selectImports方法,handle方法如下:

此時deferredImportSelectors不為null,DeferredImportSelector的實作類被封裝成DeferredImportSelectorHolder,添加到deferredImportSelectors當中,
看著集合的名字就有延遲注入的意思,在ConfigurationClassParser的parse方法中遍歷完了所有的候選集,則會呼叫this.deferredImportSelectorHandler.process(),
process方法如下:
遍歷deferredImportSelectors集合,每個都呼叫register方法,這里將AutoConfigurationImportSelector的內部類AutoConfigurationGroup添加到groupings集合當中,并將對應的配置類添加到configurationClasses當中,
兩個Map的結果如下:
遍歷完deferredImportSelectors之后,呼叫handler.processGroupImports()
遍歷之前放在groupings中的DeferredImportSelectorGrouping物件,呼叫getImports方法,該方法回傳的是延遲注入的類名封裝成的Entry結點的迭代器物件,
遍歷延遲注入類,呼叫process方法處理,該方法得到自動配置結點,將其添加到autoConfigurationEntries集合當中,再遍歷自動配置結點的所有配置類的類名,添加到entries集合當中,
getAutoConfigurationEntry方法用于得到autoConfigurationEntry,該物件管理了自動配置的類名,getAutoConfigurationEntry方法代碼如下:
這里的attributes是根據注解的元資料得到的兩個屬性exclude和excludeName,前面介紹過了,就是用來移除掉指定自動配置類的自動裝配,呼叫getCandidateConfigurations方法得到候選自動配置類的類名,
之后就是根據attributes來過濾得到的configurations,
接著呼叫fireAutoConfigurationImportEvents,自動加載類路徑下Jar包中META-INF/spring.factories檔案中 AutoConfigurationImportListener的實作類,并觸發該介面的onAutoConfigurationImportEvent方法,
getCandidateConfigurations方法如下:

這里有個空判斷言,意思是在META-INF/spring.factories下沒有找到自動配置類,如果你使用了自定義的包,確保檔案是正確的,說明要匯入的自動配置類,都記錄在了META-INF/spring.factories當中,隨便打開一個該檔案,就可以看到要自動裝配的配置類,

loadFactoryNames方法如下:
此時快取里竟然已經有了需要的東西,重新打斷點,再一次除錯發現,在Spring容器啟動的時候,構造方法里面就已經會獲取SpringFactoriesInstances,加載META-INF/spring.factories目錄下的自動配置類,
loadSpringFactories方法如下:
FACTORIES_RESOURCE_LOCATION是一個常量,等于"META-INF/spring.factories",遍歷目錄下的該檔案夾,例如下圖是屬于/Users/gongsenlin/MavenRespository/org/springframework/boot/spring-boot/2.3.5.RELEASE/spring-boot-2.3.5.RELEASE.jar!/META-INF/spring.factories檔案的屬性結果,
都添加到cache快取當中,
繼續回到getImports方法,上面分析完了process方法,接下來就是呼叫group的selectImports方法,回傳那些應該被注入的類,

利用之前已經保存在autoConfigurationEntries和entries集合當中資料,篩選出要注入的類,
結果如下:
getImports()方法結束,回到了processGroupImports()
遍歷上面的31個要注入的類,呼叫processImports方法,該方法在之前的博客《Spring中@Import注解的使用和實作原理》中分析過了,這里不再贅述,至此就完成了自動裝配,
3.自定義啟動器
根據上面的的分析,可以想到要實作自動裝配,需要撰寫一個spring.factories檔案,放在META-INF目錄下,就可以將某個類,交給Spring來管理,
再配合上@Configuration注解、@Bean注解、@Import注解等,就可以實作簡單的自動裝配,
starter模塊:只需要將其他自動配置模塊,或依賴的模塊添加到pom檔案中,
如下:
autoconfigure模塊目錄結構如下:

其中配置類是MyAutoConfigure,代碼如下:
使用@Configuration注解表明這是一個配置類,配合@Import注解和@Bean注解將A類和B類注入Spring容器當中,
META-INF/spring.factories檔案不能少,內容如下:
這樣就完成了一個簡單的自定義啟動器,
將這個starter啟動器的依賴,添加到需要的專案pom檔案當中,就可以實作自動裝配了,
啟動boot-02專案,A類和B類實作了自動裝配,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/263807.html
標籤:java
