Springboot自動裝配過程
什么是Springboot自動裝配?
我們使用ssm開發(fā)的時候,需要寫spring.xml或者配置bean,需要配置<bean/>、@Bean把類交給工廠管理。而反觀springboot開發(fā)的時候,只需要配置一些簡單的.properties(.yml)就行了。其實springboot就是把這些我們以前要寫的<bean/>、@Bean封裝成啟動器(starter)導(dǎo)入交給工廠了。
需要了解springboot的自動裝配過程,必須從如下幾個相關(guān)注解開始
- @SpringBootApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}
@Target({ElementType.TYPE}):注解可以標(biāo)記在哪;
@Retention(RetentionPolicy.RUNTIME):注解標(biāo)注的類編譯以什么方式保留;
@Documented:生成文檔信息的時候保留注解,對類作輔助說明;
@Inherited:繼承關(guān)系;
@SpringBootConfiguration:Spring Boot的配置類;
@EnableAutoConfiguration:開啟自動配置功能;
@ComponentScan(包路徑):掃描包;
excludeFilters:排除策略;
如:@ComponentScan(basePackages = "com.not_lsj",
excludeFilters = {@ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = {"com.not_lsj.controller.*"})})
掃描 com.not_lsj 包,但是把controller包下所有的類都排除掉,不參與掃描
- @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
}
@AutoConfigurationPackage:將當(dāng)前配置類所在包保存在BasePackages的Bean中,供Spring內(nèi)部使用。就是注冊了一個bean,這個bean用來保存當(dāng)前配置類的;
@Import({AutoConfigurationImportSelector.class}):導(dǎo)入當(dāng)前類;
-
Conditional衍生注解
作用:條件注解,必須是@Conditional指定的條件成立,才給容器中添加組件,組件才生效。
列舉幾種常見的:
@ConditionalOnBean:容器中存在指定Bean
@ConditionalOnMissingBean:容器中不存在指定Bean
@ConditionalOnClass:系統(tǒng)中有指定的類
@ConditionalOnMissingClass:系統(tǒng)中沒有指定的類
@ConditionalOnProperty:系統(tǒng)中指定的屬性是否有指定的值
@ConditionalOnWebApplication:當(dāng)前是web環(huán)境
......
順便說一下,spring創(chuàng)建bean的幾種方式:
- XML解析
- 注解(@Configuration、@Component(及其衍生注解)、@Bean)
- ClassPathBeanDefinitionScanner掃描特定路徑下的具有注解的Bean 進(jìn)行注冊
- @import
在此介紹一下@Import注解:作用是導(dǎo)入某個類(一般用于封裝)
A.class是一個普通類
- @Import(A.class):直接導(dǎo)入某個類
B implements importSelector
- @Import(B.class):一次導(dǎo)入多個類
importSelector有一個子接口 DeferredImportSelector
C implements importBeanDefinitionRegister
- @Import(C.class):導(dǎo)入一個定制的類
Springboot就是用的@Import注解實現(xiàn)自動配置,實現(xiàn)了DeferredImportSelector接口
- DeferredImportSelector
有兩大特性
- 延遲加載:spring源碼中解析@Import注解,都是再其他注解解析完后。
如:某個Bean在容器中通過@Bean注冊了,我們需要定制同一個Bean,這時就需要延遲加載了,因為會通過@ConditionOnBean..校驗當(dāng)前容器是否存在這個Bean,如果存在,我們定制的就不生效。- 分組:當(dāng)前類導(dǎo)入的Bean,都是與Spring容器里面的Bean分開來的,排序只是在本組內(nèi);
如:當(dāng)前spring自己也有很多Bean要注冊,然后我們通過DeferredImportSelector又導(dǎo)入一組Bean,這兩組Bean時互補(bǔ)干擾的,相反如果他們沒有分組,會導(dǎo)致容器的與我們定制的不能保證加載順序,上訴的@ConditionOnBean..校驗會存在問題;如果先加載我們定制的Bean,而到容器加載的時候,發(fā)現(xiàn)存在這個Bean就會報錯。
實現(xiàn)類的方法執(zhí)行順序:
先調(diào)用getImportGroup()
有返回值:執(zhí)行返回值的內(nèi)容 -->process() -->selectImports(),會實現(xiàn)排序
無返回值:執(zhí)行父類的selectImports()導(dǎo)入Bean
- 部分實現(xiàn)的源碼
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
});
// getAutoConfigurationEntry進(jìn)行掃描具有META-INF/spring.factories文件的jar
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
while(var4.hasNext()) {
String importClassName = (String)var4.next();
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
通過@Import導(dǎo)入一個DeferredImportSelector類型的類:AutoConfigurationImportSelector
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 獲取EnableAutoConfiguration注解屬性信息
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 從META‐INF/spring.factories中獲得候選的自動配置類(?。。。? List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 排重
configurations = this.removeDuplicates(configurations);
//根據(jù)EnableAutoConfiguration注解中屬性,獲取不需要自動裝配的類名單
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 根據(jù):@EnableAutoConfiguration.exclude
// @EnableAutoConfiguration.excludeName
// spring.autoconfigure.exclude 進(jìn)行排除
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 通過讀取spring.factories 中的OnBeanCondition\OnClassCondition\OnWebApplicationCondition進(jìn)行過濾
configurations = this.getConfigurationClassFilter().filter(configurations);
// 這個方法是調(diào)用實現(xiàn)了AutoConfigurationImportListener的bean.. 分別把候選的配置名單,和排除的配置名單傳進(jìn)去做擴(kuò)展
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
// 最后排序并注冊Bean
public Iterable<Entry> selectImports() {}
每一個springboot的應(yīng)用,都會導(dǎo)入一個spring-boot-autoconfigure模塊,而META-INF/spring.factories文件就在這個模塊下面。而校驗自動裝配生效的是org.springframework.boot.autoconfigure.EnableAutoConfiguration,如圖:

說明:在springboot啟動類內(nèi)部執(zhí)行run時,會執(zhí)行selectImports()方法,找到對應(yīng)的配置類的權(quán)限定名對應(yīng)的class,加載到spring容器中。
最后看個例子:上面截圖的AopAutoConfiguration
@Configuration(
proxyBeanMethods = false // @Configuration表示是一個配置類, proxyBeanMethods = false表示當(dāng)前類不是代理類(默認(rèn)true)
)
@ConditionalOnProperty( // 表示當(dāng)前項目環(huán)境必須存在某種配置
prefix = "spring.aop",
name = {"auto"}, // 必須spring.aop.auto配置
havingValue = "true",
matchIfMissing = true // 如果等于false,不存在spring.aop.auto,aop不生效
) // 但是這里等于true表示不存在spring.aop.auto配置,就是用默認(rèn)的aop
public class AopAutoConfiguration {
}
pom.xml引入xxx-starter,自動裝配xxx功能,根據(jù)定義的規(guī)則校驗成功之后,就是將所需的類導(dǎo)入spring工廠管理,完成自動裝配。
-
流程圖
springboot-自動裝配.png
總結(jié):
- 首先通過@Import導(dǎo)入一個DeferredImportSelector類型的類
- 掃描所有jar中的META‐INF/spring.factories
- 得到所有的有類路徑的Bean組成List進(jìn)行排序加載
- 最后spring就能管理這些類了
- 自定義啟動器(starter)
通過對springboot自動配置原理的了解,只要根據(jù)springboot規(guī)則,通過org.springframework.boot.autoconfigure.EnableAutoConfiguration導(dǎo)入,就能制定我們自定義的starter:
規(guī)則:
啟動器(starter)就是空的jar文件,用來提供輔助性依賴管理。
自定義一個spring-boot-autoconfigure的配置模塊,寫配置類
命名:
官方:spring-boot-starter-xxx
非官方:xxx-spring-boot-starter(mybatis)
實現(xiàn)步驟:
構(gòu)建一個管理父項目(類是微服務(wù)的父項目)
構(gòu)建xxx-spring-boot-autoconfigure模塊實現(xiàn)配置類
構(gòu)建xxx-spring-boot-starter提供調(diào)用
自定義starter案例:https://gitee.com/not-lsj/spring-boot-starter.git
結(jié)尾:
- 本文介紹了springboot自動裝配原理
- 模擬實現(xiàn)了自定義starter
- 某個時刻會把springboot啟動原理總結(jié)發(fā)布和兩篇文章的總結(jié)流程圖奉上
- 歡迎來交流
最后的最后:第一次寫總結(jié),可能會不太好,但是會越來越努力!??!
