Spring Boot 深入分析AutoConfigurationImportFilter自動(dòng)化條件配置源碼
1. AutoConfigurationImportFilter的作用
之前講解了SpringBoot的Conditional的自動(dòng)化條件配置,我們分析了內(nèi)部是如何具體實(shí)現(xiàn),在整個(gè)實(shí)現(xiàn)當(dāng)中, 還有一個(gè)很重要的接口, AutoConfigurationImportFilter是它的前置調(diào)用, 它是一個(gè)過(guò)濾器接口,我們?cè)僮錾钊胙芯浚?看下是如何控制處理這么多條件注解, 又是怎樣過(guò)濾處理的,從性能效率又做了哪些處理?
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); }
從說(shuō)明可以看到,該類主要功能是過(guò)濾那些在spring.factories配置文件中定義的自動(dòng)化配置項(xiàng), 還有一個(gè)重要作用是在自動(dòng)化配置類的字節(jié)碼加載之前進(jìn)行攔截過(guò)濾,提升處理效率, 節(jié)省資源開(kāi)銷。
2. AutoConfigurationImportFilter UML類圖說(shuō)明
從圖中可以看到, AutoConfigurationImportFilter一共有三個(gè)實(shí)現(xiàn)類(OnBeanCondition、OnClasssCondition、OnWebApplicationCondition),三個(gè)類都是通過(guò)FilteringSpringBootCondition抽象父類間接實(shí)現(xiàn),AutoConfigurationImportFilter在所有OnXXXCondition條件注解類的上層,這樣大概就能看出它們的調(diào)用棧的關(guān)聯(lián)關(guān)系, 經(jīng)過(guò)研究代碼, Spring Boot 會(huì)先調(diào)用AutoConfigurationImportFilter的match方法做過(guò)濾處理, 后面再通過(guò)loadBeanDefinitions觸發(fā)Condition的matches方法做條件判斷。
3. FilteringSpringBootCondition抽象類
FilteringSpringBootCondition是一個(gè)抽象類, 它繼承SpringBootCondition,實(shí)現(xiàn)AutoConfigurationImportFilter的match接口, 內(nèi)部調(diào)用抽象方法getOutcomes負(fù)責(zé)具體的過(guò)濾邏輯處理。
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) { // 獲取條件化判斷報(bào)告, 用于記錄處理結(jié)果 ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory); // 獲取具體匹配處理結(jié)果, 由抽象方法getOutcomes負(fù)責(zé)具體實(shí)現(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對(duì)象, 用于記錄處理結(jié)果。
- 調(diào)用getOutcomes方法, 這個(gè)一個(gè)抽象方法, 返回匹配處理結(jié)果, 由上面UML圖中的OnXXXCondition等類負(fù)責(zé)具體實(shí)現(xiàn)。
- 接下來(lái)創(chuàng)建match數(shù)組, 布爾值標(biāo)記處理結(jié)果。
- 下面還會(huì)調(diào)用logOutcome方法, 做日志打印處理。調(diào)用recordConditionEvaluation, 記錄匹配結(jié)果。
除了match方法, FilteringSpringBootCondition下還有個(gè) filter 方法。
Filter方法, protected修飾, 實(shí)際上會(huì)由OnXXXCondition的getOutcomes方法調(diào)用, 從UML關(guān)系圖可以看到, 實(shí)際是由子類處理邏輯實(shí)現(xiàn)過(guò)程中調(diào)用。
protected List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter, ClassLoader classLoader) { // 校驗(yàn), 為空判斷 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; }
從源碼可以看到,先做簡(jiǎn)單的為空判斷, 具體則是通過(guò)classNameFilter的match方法做處理。
我們?cè)倏聪翪lassNameFilter的源碼:
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); } }; // 抽象方法, 有子類負(fù)責(zé)具體匹配邏輯實(shí)現(xiàn) public abstract boolean matches(String className, ClassLoader classLoader); // 判斷指定的類, 是否能夠通過(guò)指定的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, 兩種類型為相反邏輯, 通過(guò)isPresent方法做判斷,里面則根據(jù)ClassLoader, 如果不為空, 則加載目標(biāo)CLASS,處理沒(méi)有報(bào)錯(cuò), 則返回true值。
講到這里, Filter的作用是什么?ClassNameFilter兩種類型有什么意義? 我們舉個(gè)例子說(shuō)明, @ConditionalOnClass和@ConditionalOnMissingClass兩個(gè)注解,判斷條件是指定的CLASS是否存在。 如果采用ConditionalOnClass注解, 那么采用PRESENT存在優(yōu)先規(guī)則, 如果采用ConditionalOnMissingClass注解, 那么采用MISSING缺失優(yōu)先規(guī)則。
4. AutoConfigurationImportSelector類
再分析一下AutoConfigurationImportSelector這個(gè)類, 這是一個(gè)自動(dòng)配置導(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ù)配置上下文, 獲取所有需要處理的自動(dòng)化配置類信息, 也就是所有的auotconfigration實(shí)現(xiàn)類 String[] candidates = StringUtils.toStringArray(configurations); boolean[] skip = new boolean[candidates.length]; boolean skipped = false; // 遍歷處理, 通過(guò)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]) { // 如果沒(méi)有匹配, skip標(biāo)記為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]) { // 記錄需要處理的自動(dòng)化配置信息 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); } ... }
可以看到, 通過(guò)getAutoConfigurationImportFilters()加載過(guò)濾器, 在調(diào)用過(guò)濾器的match執(zhí)行邏輯處理。條件匹配處理完成之后, 如果完全匹配, 則直接返回Configuration信息, 否則, 記錄需要處理的自動(dòng)化配置信息并做返回。 Configuration信息實(shí)際就是Spring Boot內(nèi)置的一百多個(gè)自動(dòng)化配置類:
這里也就是根據(jù)條件去過(guò)濾判斷, 哪些AutoConfiguration符合規(guī)則, 哪些不符合規(guī)則, 只有符合規(guī)則的自動(dòng)化配置類才會(huì)進(jìn)入加載流程,實(shí)現(xiàn)對(duì)應(yīng)的組件功能。
5. 總結(jié)
AutoConfigurationImportFilter是Spring Boot條件化注解的核心過(guò)濾器接口,這個(gè)類在啟動(dòng)的時(shí)候通過(guò)SPI機(jī)制實(shí)現(xiàn),在Spring Boot的條件化配置中會(huì)進(jìn)行回調(diào). 基于Conditional的自動(dòng)化配置主要流程就分析到這里, 細(xì)節(jié)上就不再贅述, 大家有空可以再跟蹤源碼深入研究,了解更為細(xì)節(jié)的自動(dòng)化配置的處理邏輯。
到此這篇關(guān)于Spring Boot 深入分析AutoConfigurationImportFilter自動(dòng)化條件配置源碼的文章就介紹到這了,更多相關(guān)Spring Boot AutoConfigurationImportFilter內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot實(shí)現(xiàn)Java郵件任務(wù)過(guò)程解析
這篇文章主要介紹了Springboot實(shí)現(xiàn)Java郵件任務(wù)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09Springboot+MyBatist實(shí)現(xiàn)前后臺(tái)交互登陸功能方式
這篇文章主要介紹了Springboot+MyBatist實(shí)現(xiàn)前后臺(tái)交互登陸功能方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java連接并操作Sedna XML數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Java連接并操作Sedna XML數(shù)據(jù)庫(kù)的方法,較為詳細(xì)的說(shuō)明了Sedna XML數(shù)據(jù)庫(kù)的原理與功能,并給出了基于java操作Sedna XML數(shù)據(jù)庫(kù)的方法,需要的朋友可以參考下2015-06-06java實(shí)現(xiàn)微信公眾平臺(tái)發(fā)送模板消息的示例代碼
這篇文章主要介紹了java實(shí)現(xiàn)微信公眾平臺(tái)發(fā)送模板消息的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09SpringBoot?攔截器返回false顯示跨域問(wèn)題
這篇文章主要介紹了SpringBoot?攔截器返回false顯示跨域問(wèn)題,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,需要的小伙伴可以參考一下2022-04-04Java如何獲取數(shù)組和字符串的長(zhǎng)度(length還是length())
這篇文章主要介紹了Java如何獲取數(shù)組和字符串的長(zhǎng)度(length還是length()),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12擲6面骰子6000次每個(gè)點(diǎn)數(shù)出現(xiàn)的概率
今天小編就為大家分享一篇關(guān)于擲6面骰子6000次每個(gè)點(diǎn)數(shù)出現(xiàn)的概率,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02