tips:不同版本代碼實現有差異。
前面兩章了解的流程,就是 SpringBoot 自動轉配的核心。
一、自動裝配
1.1 什么是 SpringBoot 自動裝配?
自動裝配是 Spring 框架用來減少配置的顯式需求而引入的一個特性,該特性通過 @Autowired
或者@Resource
注解實現依賴對象的自動注入。而 Spring Boot 在此基礎上進一步發展,提出了更高級的自動配置(Auto-configuration)概念。SpringBoot 在啟動時會掃描外部 jar 包中的META-INF/spring.factories文件,將文件中配置的類型信息加載到 Spring 容器,對于外部 jar 來說,只需要按照 SpringBoot 定義的標準,就能將自己的功能裝配到 Spring 容器里面。
自動裝配機制是Spring Boot提供的核心特性之一,它極大地提高了開發效率,同時確保了配置的簡潔性和靈活性,實現“約定大于配置”
1.2 自動裝配的工作原理
自動裝配,抓住三個要素。(在前面章節我們已經分析了其核心流程)
- @
EnableAutoConfiguration
- AutoConfigurationImportSelector
spring.factories
AutoConfigurationImportSelector
是自動裝配背后的關鍵角色。實現自ImportSelector
接口,它會從 META-INF/spring.factories
文件中加載EnableAutoConfiguration
指定的配置類。這是通過SpringFactoriesLoader
類來實現的,它為Spring Boot自動裝配提供了加載和解析spring.factories
文件的能力。
spring.factories
: 位于META-INF
目錄下的spring.factories
文件包含了自動配置類的全限定名列表。當應用啟動時,這些配置類會被加載并根據條件判斷是否應用到當前的應用上下文中。
@EnableAutoConfiguration 作為自動裝配的關鍵。
二、從注解入手
以 mybatis-starter-apply 為例子開始
gitee地址:uzong-starter-learning: 學習 SpringBoot Starter 的工程案例
在我們啟動類上有這樣的注解 SpringBootApplication,它是開啟配置自動裝配的大門鑰匙。
@SpringBootApplication
@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}
分析 @SpringBootApplication 注解, 它是多個注解的集成。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
- @Configuration: SpringBootConfiguration 等于 Configuration。屬于派生。
- @EnableAutoConfiguration: 負責激活 SpringBoot 自動裝配機制
- @ComponentScan: 路徑掃描,該掃描路徑下的 bean 都會被掃描加載,另外可以排除不需要的類。 即激活 @Conponent 的掃描
@SpringBootApplication 等價于上面三個注解。而實現自動裝配的關鍵注解是 @EnableAutoConfiguration
@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
- @Import 導入 AutoConfigurationImportSelector 配置類,它會加載各種自動化配置類, 它是實現 Starter 的核心
- @AutoConfigurationPackag: 由AutoConfigurationPackages.Registrar(一個實現了ImportBeanDefinitionRegistrar的類)處理。默認值是啟動類所在的包路徑,默認指定啟動類路徑下的類加載到 Spring 容器。主要邏輯在 AutoConfigurationPackages#register 方法中。 該方法有兩個參數 registry 和 packageNames。packageNames 的值默認是啟動類包所在的路徑。如下所示:
對應的棧幀
registry 即 DefaultListableBeanFactory
Lite && Full (拓展理解)
@Bean 的聲明方式為“輕量模式 Lite”
@Configuration 下聲明的@Bean為“完全模式Full”,存在 cglib 提升
三、AutoConfigurationImportSelector
關于 AutoConfigurationImportSelector, 它實現了 ImportSelector 接口(3.x 開始)
3.1 ImportSelector
public interface ImportSelector {/*** Select and return the names of which class(es) should be imported based on* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.*/String[] selectImports(AnnotationMetadata importingClassMetadata);}
ImportSelector
用于條件性的導入配置類。是Spring框架處理@Import
注解的一部分,提供了一種靈活的方式來根據運行時的條件動態決定 Spring 配置; 尤其是在開發自定義自動配置和框架擴展時。
selectImports
方法的返回值是一個包含配置類全路徑名稱(Fully qualified names
)的字符串數組
ImportSelector
可以與注解一起使用,讓開發者在@Configuration
注解的類上通過@Import
來導入它。它不直接將配置類加入到Spring上下文中,而是提供一種選擇性地導入配置類的方式。
通過ImportSelector
,開發者可以在運行時根據條件(比如classpath中是否存在某個類,或者某個屬性是否被定義等)來決定是否導入某些配置。這提供了極大的靈活性,使得 Spring Boot 的自動配置成為可能。
開發一個可插拔的模塊或框架時,ImportSelector
可以用來根據應用的配置或依賴來動態導入配置類。Spring Boot 大量使用了ImportSelector
來實現自動配置。通過條件檢查,只有當滿足特定條件時,如果類路徑中有特定的類存在,或某些屬性被定義,相關的配置類才會被導入
3.2 DeferredImportSelector
/*** A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans* have been processed. This type of selector can be particularly useful when the selected* imports are {@code @Conditional}.** <p>Implementations can also extend the {@link org.springframework.core.Ordered}* interface or use the {@link org.springframework.core.annotation.Order} annotation to* indicate a precedence against other {@link DeferredImportSelector}s.** @author Phillip Webb* @since 4.0*/
public interface DeferredImportSelector extends ImportSelector {}
A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans
have been processed
DeferredImportSelector
是ImportSelector
的一個擴展接口,它在常規的ImportSelector
執行之后稍晚運行。這允許其他的配置類先被處理,使得DeferredImportSelector
可以在做決定時考慮到這些先前的配置。
而 AutoConfigurationImportSelector 就是實現了 DeferredImportSelector
該接口。
ImportSelector
是Spring框架中一個強大的特性,它為條件化配置和自動配置提供了強大的支撐,是實現靈活、動態導入Spring配置的關鍵機制。通過ImportSelector
和它的擴展,Spring實現了一套強大的配置和自動配置機制,極大地簡化了Spring應用的配置工作。
3.3 AutoConfigurationImportSelector
核心方法 selectImports
自動配置機制能夠有條件地導入合適的配置,這對整合第三方庫、開發自定義 starter 或在運行時動態調整配置都是至關重要的
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {// 檢查自動配置是否啟用,如果沒有就直接返回一個空的導入數組。if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}try {// 加載自動配置元數據,可能包括配置類的屬性、條件以及其他相關信息。AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);// 取得 @EnableAutoConfiguration 注解的屬性,這可能含有排除某些配置類的信息。AnnotationAttributes attributes = getAttributes(annotationMetadata);// 獲取所有候選的自動配置類列表。List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);// 移除配置類列表中的重復元素。configurations = removeDuplicates(configurations);// 基于AutoConfigurationMetadata將配置類列表排序。configurations = sort(configurations, autoConfigurationMetadata);// 獲取所有排除的配置類。Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 校驗排除的類是否真正存在于候選配置中,并拋出異常如果有必要。checkExcludedClasses(configurations, exclusions);// 從候選列表中移除所有排除的類。configurations.removeAll(exclusions);// 過濾掉那些不應被導入的配置類。configurations = filter(configurations, autoConfigurationMetadata);// 觸發一系列自動配置導入事件。fireAutoConfigurationImportEvents(configurations, exclusions);// 將配置類列表轉換為字符串數組返回。return StringUtils.toStringArray(configurations);}......
}
3.3 isEnabled
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,true);
}
return true;
}
首先調用 isEnabled 方法去判斷自動化配置到底有沒有開啟,可以通過在 application.properties 中配置 spring.boot.enableautoconfiguration=false 來關閉所有的自動化配置。
3.4 getCandidateConfigurations
獲取 claspath\:META-INF/spring.factories 中所有的自動裝配類。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());Assert.notEmpty(configurations,"No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
}
注意:getSpringFactoriesLoaderFactoryClass(), 返回的是 EnableAutoConfiguration.class。
到這里,我們應該知道 spring.factories 的前綴,為什么要定義成org.springframework.boot.autoconfigure.EnableAutoConfiguration
這種固定key了吧。
3.5 removeDuplicates
除候選自動化配置類中重復的類。借助 LinkedHashSet 去除重復
protected final <T> List<T> removeDuplicates(List<T> list) {return new ArrayList<>(new LinkedHashSet<>(list));
}
3.6 getExclusions
protected Set<String> getExclusions(AnnotationMetadata metadata,AnnotationAttributes attributes) {Set<String> excluded = new LinkedHashSet<>();excluded.addAll(asList(attributes, "exclude"));excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));excluded.addAll(getExcludeAutoConfigurationsProperty());return excluded;}
getExclusions 獲取到所有被排除的自動化配置類。
- 當前注解的 exclude 屬性獲取
- 當前注解的 excludeName 屬性獲取
- getExcludeAutoConfigurationsProperty() 從配置文件中的 spring.autoconfigure.exclude 屬性獲取
3.7 checkExcludedClasses
校驗不是 not auto-configuration 類則不能使用 exclude 方式做類的排除。
3.8 filter
加載完所有的自動化配置類了,但是,這些配置類是否生效,還需要根據當前項目的依賴等進行加載生效。
比如:MybatisAutoConfiguration 是否能夠加載,需要查看當前的依賴是否存在,比如 SqlSessionFactory.class
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {invokeAwareMethods(filter);boolean[] match = filter.match(candidates, autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {skip[i] = true;skipped = true;}}
}
處理下面兩個注解
- ConditionalOnClass
- ConditionalOnMissingClass
3.9 其他方法
fireAutoConfigurationImportEvents:于那些想要在導入配置之前進行特定操作的擴展點。
3.10 小結
到這里,關于 SpringBoot 的Starter 能夠進行擴展,來源于 ImportSelector 實現類。 它是能夠實現擴展的核心。AutoConfigurationImportSelector 是 SpringBoot 中的類, 但是 ImportSelector 是springframework core 中的類。
這些 Configuration 類,將使用 ConfigurationClassParse 進行解析。
四、 ConfigurationClassParse
processDeferredImportSelectors 導入配置類。
private void processDeferredImportSelectors() {for (DeferredImportSelectorHolder deferredImport : deferredImports) {ConfigurationClass configClass = deferredImport.getConfigurationClass();try {String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);}......}
}
五、本章小結
本章能夠實現擴展,是來源于 ImportSelector(DeferredImportSelector) 接口。AutoConfigurationImportSelector(SpringBoot) 整好擴展這個類,使得 Starter 模式能夠非常優雅的進行擴展。
那么下一章,我們將進一步擴展理解 ConfigurationClassParse 這部分源碼。
已同步發布到公眾號:面湯放鹽?第六節 自動裝配源碼理解 (qq.com)
掘金賬號:第六節 自動裝配源碼理解 - 掘金 (juejin.cn)