Spring Boot 深入分析AutoConfigurationImportFilter自動化條件配置源碼
1. AutoConfigurationImportFilter的作用
之前講解了SpringBoot的Conditional的自動化條件配置,我們分析了內(nèi)部是如何具體實現(xiàn),在整個實現(xiàn)當(dāng)中, 還有一個很重要的接口, AutoConfigurationImportFilter是它的前置調(diào)用, 它是一個過濾器接口,我們再做深入研究, 看下是如何控制處理這么多條件注解, 又是怎樣過濾處理的,從性能效率又做了哪些處理?
AutoConfigurationImportFilter的源碼:
@FunctionalInterface
public interface AutoConfigurationImportFilter {
/**
* Apply the filter to the given auto-configuration class candidates.
* @param autoConfigurationClasses the auto-configuration classes being considered.
* This array may contain {@code null} elements. Implementations should not change the
* values in this array.
* @param autoConfigurationMetadata access to the meta-data generated by the
* auto-configure annotation processor
* @return a boolean array indicating which of the auto-configuration classes should
* be imported. The returned array must be the same size as the incoming
* {@code autoConfigurationClasses} parameter. Entries containing {@code false} will
* not be imported.
*/
boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata);
}從說明可以看到,該類主要功能是過濾那些在spring.factories配置文件中定義的自動化配置項, 還有一個重要作用是在自動化配置類的字節(jié)碼加載之前進行攔截過濾,提升處理效率, 節(jié)省資源開銷。
2. AutoConfigurationImportFilter UML類圖說明

從圖中可以看到, AutoConfigurationImportFilter一共有三個實現(xiàn)類(OnBeanCondition、OnClasssCondition、OnWebApplicationCondition),三個類都是通過FilteringSpringBootCondition抽象父類間接實現(xiàn),AutoConfigurationImportFilter在所有OnXXXCondition條件注解類的上層,這樣大概就能看出它們的調(diào)用棧的關(guān)聯(lián)關(guān)系, 經(jīng)過研究代碼, Spring Boot 會先調(diào)用AutoConfigurationImportFilter的match方法做過濾處理, 后面再通過loadBeanDefinitions觸發(fā)Condition的matches方法做條件判斷。
3. FilteringSpringBootCondition抽象類
FilteringSpringBootCondition是一個抽象類, 它繼承SpringBootCondition,實現(xiàn)AutoConfigurationImportFilter的match接口, 內(nèi)部調(diào)用抽象方法getOutcomes負責(zé)具體的過濾邏輯處理。
abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
// bean 工廠
private BeanFactory beanFactory;
// bean 加載器
private ClassLoader beanClassLoader;
//
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
// 獲取條件化判斷報告, 用于記錄處理結(jié)果
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
// 獲取具體匹配處理結(jié)果, 由抽象方法getOutcomes負責(zé)具體實現(xiàn)
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
// 遍歷條件處理結(jié)果
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
// 日志打印記錄
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
// 記錄匹配處理結(jié)果
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
...
}- 先獲取ConditionEvaluationReport對象, 用于記錄處理結(jié)果。
- 調(diào)用getOutcomes方法, 這個一個抽象方法, 返回匹配處理結(jié)果, 由上面UML圖中的OnXXXCondition等類負責(zé)具體實現(xiàn)。
- 接下來創(chuàng)建match數(shù)組, 布爾值標記處理結(jié)果。
- 下面還會調(diào)用logOutcome方法, 做日志打印處理。調(diào)用recordConditionEvaluation, 記錄匹配結(jié)果。
除了match方法, FilteringSpringBootCondition下還有個 filter 方法。
Filter方法, protected修飾, 實際上會由OnXXXCondition的getOutcomes方法調(diào)用, 從UML關(guān)系圖可以看到, 實際是由子類處理邏輯實現(xiàn)過程中調(diào)用。
protected List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter,
ClassLoader classLoader) {
// 校驗, 為空判斷
if (CollectionUtils.isEmpty(classNames)) {
return Collections.emptyList();
}
// 記錄match匹配結(jié)果
List<String> matches = new ArrayList<>(classNames.size());
// 遍歷處理
for (String candidate : classNames) {
// 從指定的classLoader中加載class,再根據(jù)ClassNameFilter類型, 返回最終結(jié)果
if (classNameFilter.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}從源碼可以看到,先做簡單的為空判斷, 具體則是通過classNameFilter的match方法做處理。
我們再看下ClassNameFilter的源碼:
protected enum ClassNameFilter {
// 兩種類型, 當(dāng)前存在優(yōu)先, 如果classLoader中能夠加載指定類, 返回true
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},
// 缺失優(yōu)先規(guī)則, 即便在classLoader中能夠加載指定類, 也是返回false
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
// 抽象方法, 有子類負責(zé)具體匹配邏輯實現(xiàn)
public abstract boolean matches(String className, ClassLoader classLoader);
// 判斷指定的類, 是否能夠通過指定的classLoader加載
public static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
// 類的加載處理
private static Class<?> forName(String className, ClassLoader classLoader) throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
}
}從中可以看出, 這里面有兩種形式判斷,一種是PRESENT, 另外一種是MISSING, 兩種類型為相反邏輯, 通過isPresent方法做判斷,里面則根據(jù)ClassLoader, 如果不為空, 則加載目標CLASS,處理沒有報錯, 則返回true值。
講到這里, Filter的作用是什么?ClassNameFilter兩種類型有什么意義? 我們舉個例子說明, @ConditionalOnClass和@ConditionalOnMissingClass兩個注解,判斷條件是指定的CLASS是否存在。 如果采用ConditionalOnClass注解, 那么采用PRESENT存在優(yōu)先規(guī)則, 如果采用ConditionalOnMissingClass注解, 那么采用MISSING缺失優(yōu)先規(guī)則。
4. AutoConfigurationImportSelector類
再分析一下AutoConfigurationImportSelector這個類, 這是一個自動配置導(dǎo)入選擇處理器,在AutoConfigurationImportFilter的match方法之前調(diào)用, 是屬于上層調(diào)用。先由springFactores加載選擇處理器, 主要包含OnClassCondition、OnWebApplicationCondition和OnBeanCondition等, 再做具體的條件判斷處理。 我們了解下它的處理邏輯:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
// 根據(jù)配置上下文, 獲取所有需要處理的自動化配置類信息, 也就是所有的auotconfigration實現(xiàn)類
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 遍歷處理, 通過getAutoConfigurationImportFilters方法, 獲取springFactores中的選擇處理器, 包含OnClassCondition、OnWebApplicationCondition和OnBeanCondition。
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 填充filter信息
invokeAwareMethods(filter);
// 獲取filter的匹配結(jié)果
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
// 如果沒有匹配, skip標記為true
skip[i] = true;
// 清除該auotconfigration記錄信息
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
// 完全匹配, 直接返回configurations數(shù)據(jù)
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
// 記錄需要處理的自動化配置信息
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
// 是否需要日志打印追蹤
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList<>(result);
}
...
}可以看到, 通過getAutoConfigurationImportFilters()加載過濾器, 在調(diào)用過濾器的match執(zhí)行邏輯處理。條件匹配處理完成之后, 如果完全匹配, 則直接返回Configuration信息, 否則, 記錄需要處理的自動化配置信息并做返回。 Configuration信息實際就是Spring Boot內(nèi)置的一百多個自動化配置類:

這里也就是根據(jù)條件去過濾判斷, 哪些AutoConfiguration符合規(guī)則, 哪些不符合規(guī)則, 只有符合規(guī)則的自動化配置類才會進入加載流程,實現(xiàn)對應(yīng)的組件功能。
5. 總結(jié)
AutoConfigurationImportFilter是Spring Boot條件化注解的核心過濾器接口,這個類在啟動的時候通過SPI機制實現(xiàn),在Spring Boot的條件化配置中會進行回調(diào). 基于Conditional的自動化配置主要流程就分析到這里, 細節(jié)上就不再贅述, 大家有空可以再跟蹤源碼深入研究,了解更為細節(jié)的自動化配置的處理邏輯。
到此這篇關(guān)于Spring Boot 深入分析AutoConfigurationImportFilter自動化條件配置源碼的文章就介紹到這了,更多相關(guān)Spring Boot AutoConfigurationImportFilter內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用flyway實現(xiàn)腳本自動化的方法詳解
Flyway是一個開源的數(shù)據(jù)庫版本控制工具,主要用于管理數(shù)據(jù)庫的版本和變更,它可以自動化地將數(shù)據(jù)庫遷移到不同的版本,同時支持多種數(shù)據(jù)庫類型,本文給大家介紹了如何使用flyway實現(xiàn)腳本自動化,需要的朋友可以參考下2023-10-10
Spring Cloud Gateway全局異常處理的方法詳解
這篇文章主要給大家介紹了關(guān)于Spring Cloud Gateway全局異常處理的相關(guān)資料,需要的朋友可以參考下2018-10-10
@PathVariable注解,讓spring支持參數(shù)帶值功能的案例
這篇文章主要介紹了@PathVariable注解,讓spring支持參數(shù)帶值功能的案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02

