SpringBoot自動配置源碼深入刨析講解
自動配置底層源碼分析
本次springboot源碼來自2.6.6版本。
@EnableAutoConfiguration源碼解析
在springboot中,當(dāng)我們引入某個依賴,就可以直接使用依賴里面的類進行自動注入,不需要像ssm框架那樣在xml文件中配置各種bean,然后進行關(guān)聯(lián)。像這樣我們稱之為自動配置。那么自動配置到底配了什么?
SpringBoot中的自動配置,更多的是配置各種Bean,同時對于端口號這些配置,一些特定屬性SpringBoot也是會提供一種默認值的,也相當(dāng)于一種自動配置。
那SpringBoot是如何自動的幫助我們來配置這些Bean的呢?并且如果某些Bean程序員自己也配置了,那SpringBoot是如何進行選擇的呢?
在springboot啟動類中有@SpringBootApplication注解,該注解包含了@EnableAutoConfiguration
而@EnableAutoConfiguration的作用就是導(dǎo)入AutoConfigurationImportSelector.class這個類。在spring中的配置類源碼分析中,分析過@Import導(dǎo)入的類會當(dāng)成配置類來解析,并且如果這個配置類是實現(xiàn)了DeferredImportSelector接口,就會調(diào)用selectImports方法。這部分屬于spring源碼的知識不在贅述。
有上述類關(guān)系圖中可以看出,會調(diào)用AutoConfigurationImportSelector的selectImports方法
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { // 會在所有@Configuration都解析完了之后才執(zhí)行 if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 獲取自動配置類(spring.factories中所導(dǎo)入的) AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
而selectImports的核心代碼在于getAutoConfigurationEntry(annotationMetadata)。接下來一步步分析這個方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 獲取@EnableAutoConfiguration的屬性 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 獲取spring.factories中所有的AutoConfiguration List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去重(也就是按類名去重) configurations = removeDuplicates(configurations); // 獲取需要排除的AutoConfiguration,可以通過@EnableAutoConfiguration注解的exclude屬性,或者spring.autoconfigure.exclude來配置 Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 排除 checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 獲取spring.factories中的AutoConfigurationImportFilter對AutoConfiguration進行過濾 // 默認會拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition // 這三個會去判斷上面的AutoConfiguration是否符合它們自身所要求的條件,不符合的會過濾掉,表示不會進行解析了 // 會利用spring-autoconfigure-metadata.properties中的配置來進行過濾 // spring-autoconfigure-metadata.properties文件中的內(nèi)容是利用Java中的AbstractProcessor技術(shù)在編譯時生成出來的 configurations = getConfigurationClassFilter().filter(configurations); // configurations表示合格的,exclusions表示被排除的,把它們記錄在ConditionEvaluationReportAutoConfigurationImportListener中 fireAutoConfigurationImportEvents(configurations, exclusions); // 最后返回的AutoConfiguration都是符合條件的 return new AutoConfigurationEntry(configurations, exclusions); }
首先執(zhí)行 AnnotationAttributes attributes = getAttributes(annotationMetadata);拿到@EnableAutoConfiguration的屬性封裝成AnnotationAttributes 。List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
加載自動配置類??纯丛创a是怎么獲取的
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //核心方法 傳入EnableAutoConfiguration類和類加載器 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; } //返回EnableAutoConfiguration protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { //獲取類加載器 classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } //這個name就是EnableAutoConfiguration String factoryTypeName = factoryType.getName(); //這部分代碼具體的去加載自動配置類 return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());這部分代碼如下圖,
通過類加載去加載資源,其中紅色部分的靜態(tài)變量就是 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
。也就是說類加載器去META-INF/spring.factories里面的資源
而.getOrDefault(factoryTypeName, Collections.emptyList());
這部分就是根據(jù)factoryTypeName也就是EnableAutoConfiguration。獲取EnableAutoConfiguration的值封裝成List<String>
到此就獲取到了所有自動配置類。那么List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
這個方法就結(jié)束了。接著執(zhí)行configurations = removeDuplicates(configurations);
這部分主要用去重
protected final <T> List<T> removeDuplicates(List<T> list) { return new ArrayList<>(new LinkedHashSet<>(list)); }
接著執(zhí)行Set<String> exclusions = getExclusions(annotationMetadata, attributes);
這個方法主要是把需要排除的配置類的類名放入到集合當(dāng)中。
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { Set<String> excluded = new LinkedHashSet<>(); //獲取EnableAutoConfiguration注解的exclude屬性的值 添加到排除集合當(dāng)中 excluded.addAll(asList(attributes, "exclude")); //獲取EnableAutoConfiguration注解的excludeName屬性的值 添加到排除集合當(dāng)中 excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName"))); //從配置文件中獲取spring.autoconfigure.exclude 的值 添加到排除集合中 excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; }
往下執(zhí)行checkExcludedClasses(configurations, exclusions);
和configurations.removeAll(exclusions);
從之前獲取到的自動配置類的類名中排除掉那些需要被排除了類名。
接著執(zhí)行configurations = getConfigurationClassFilter().filter(configurations);
。將排除后的自動配置類的名稱作為入?yún)?,這部分代碼是提前判斷一些條件進行過濾掉不需要加載的自動配置類
private ConfigurationClassFilter getConfigurationClassFilter() { if (this.configurationClassFilter == null) { //獲取自動配置類的過濾器 List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters(); for (AutoConfigurationImportFilter filter : filters) { invokeAwareMethods(filter); } //將所有過濾器封裝成 ConfigurationClassFilter this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters); } return this.configurationClassFilter; } protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() { //底層從 META-INF/spring.factories中加載 AutoConfigurationImportFilter的值 return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); }
上面獲取到的過濾器就是這部分
獲取到所有過濾器后通過filter(configurations);
進行過濾
List<String> filter(List<String> configurations) { long startTime = System.nanoTime(); //把自動配置類的名稱封裝成數(shù)組 String[] candidates = StringUtils.toStringArray(configurations); boolean skipped = false; // 逐個利用AutoConfigurationImportFilter來判斷所有的自動配置類的條件是否匹配,匹配結(jié)果存在match數(shù)組中 // 先利用OnBeanCondition進行過濾 // 再利用OnClassCondition進行過濾 // 再利用OnWebApplicationCondition進行過濾 for (AutoConfigurationImportFilter filter : this.filters) { //把過濾的結(jié)果 放入到boolean的數(shù)組中 boolean[] match = filter.match(candidates, this.autoConfigurationMetadata); for (int i = 0; i < match.length; i++) { if (!match[i]) { //如果匹配失敗 排除掉該自動配置類 candidates[i] = null; skipped = true; } } } // 全部都匹配 if (!skipped) { return configurations; } // 把匹配的記錄在result集合中,最后返回 List<String> result = new ArrayList<>(candidates.length); for (String candidate : candidates) { if (candidate != null) { result.add(candidate); } } 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 result; } }
過濾完成后執(zhí)行fireAutoConfigurationImportEvents(configurations, exclusions);
這部分不重要 ,可以看成就是記錄一個日志,哪些成功的哪些被排除的。
最后執(zhí)行return new AutoConfigurationEntry(configurations, exclusions);
這部分代碼把 可以加載的自動配置類 放入到一個集合,把排除的放入到另一個集合
AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) { this.configurations = new ArrayList<>(configurations); this.exclusions = new HashSet<>(exclusions); } public List<String> getConfigurations() { return this.configurations; } public Set<String> getExclusions() { return this.exclusions; }
到此所有需要加載的自動配置類都找到了。然后再回到一開始的selectImports
方法這個方法最后返回StringUtils.toStringArray(autoConfigurationEntry.getConfigurations())
。返回所有符合自動配置類的全類名。根據(jù)@Import功能會繼續(xù)將selectImports
返回的類名,當(dāng)成配置類去加載。那么每個自動配置類就會加載到springboot當(dāng)中。
到此springboot自動配置功能就結(jié)束了。至于加載自動配置類加載過程中,根據(jù)條件注解去匹配是否能夠加載,下一篇在分析。
總結(jié)
springboot啟動類中存在@SpringBootApplication,而@SpringBootApplication中包含@EnableAutoConfiguration。@EnableAutoConfiguration中通過@Import引入AutoConfigurationImportSelector。
spring啟動的時候調(diào)用AutoConfigurationImportSelector的selectImports。該方法獲取到所有可以加載的自動配置類(此時還未加載)
獲取過程如下:
- 獲取@EnableAutoConfiguration的屬性的值封裝成AnnotationAttributes
- 獲取spring.factories中key為EnableAutoConfiguration的值作為自動配置類的名稱
- 將獲取到的所有的自動配置類的名稱進行去重
- 獲取程序員配置的需要排除的自動配置類,從上一步找到的所有自動配置類中排除掉
- 獲取spring.factories中key為AutoConfigurationImportFilter的值作為過濾器封裝成ConfigurationClassFilter
- 通過ConfigurationClassFilter初次過濾不滿足條件的自動配置類
- 把排除的自動配置類和最終可加載的自動配置類進行日志記錄
- 把排除的自動配置類和最終可加載的自動配置類分別放入到集合當(dāng)中,封裝成AutoConfigurationEntry返回
最后selectImports從AutoConfigurationEntry拿出可加載的自動配置類的名稱返回。這樣springboot就會去加載那些配置類
到此這篇關(guān)于SpringBoot自動配置源碼深入刨析講解的文章就介紹到這了,更多相關(guān)SpringBoot自動配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java BufferedWriter BufferedReader 源碼分析
本文是關(guān)于Java BufferedWriter ,BufferedReader 簡介、分析源碼 對Java IO 流深入了解,希望看到的同學(xué)對你有所幫助2016-07-07java并發(fā)容器CopyOnWriteArrayList實現(xiàn)原理及源碼分析
這篇文章主要為大家詳細介紹了java并發(fā)容器CopyOnWriteArrayList實現(xiàn)原理及源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05Springboot如何通過yml配置文件為靜態(tài)成員變量賦值
這篇文章主要介紹了Springboot如何通過yml配置文件為靜態(tài)成員變量賦值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10SpringCloud超詳細講解Feign聲明式服務(wù)調(diào)用
Feign可以把Rest的請求進行隱藏,偽裝成類似Spring?MVC的Controller一樣。不用再自己拼接url,拼接參數(shù)等等操作,一切都交給Feign去做2022-06-06