原始碼分析
SpringBoot自動配置流程
?
? 首先,我們要了解在@SpringBootApplication注解的內部,還具有@EnableAutoConfiguration,@SpringBootConfiguration,@ComponentScan三個主要注解,
@SpringBootConfiguration //標注該類是配置類,需要通過該類查找自動組態檔
@EnableAutoConfiguration //自動配置的關鍵注解 其內部就是執行自動配置的代碼
@ComponentScan(excludeFilters = {
//type : 要使用的篩選器型別 , classes 指定型別篩選器
//TypeExcludeFilter.class 篩選掉spirngBootApplication中被指定排除的配置類
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
//AutoConfigurationExcludeFilter 將配置類與spirng.factories中的EnableAutoConfiguration對應的配置類進行對比匹配, 如果一致,會被排除掉
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }
) //掃描指定包的檔案,將帶有特定注解的類注入到Bean中
public @interface SpringBootApplication {
}
@ComponentScan
- @ComponentScan注解主要用來掃描我們專案中的所有被像@service ,@Repository , @Controller,@configuration 等注解修飾的類, 將其注入到我們的IOC容器中,其中也包括我們的自動配置的檔案:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class) //表示可以重復利用@ComponentScan注解
/**
*作用 : 可以掃描指定的包,如果未指定包范圍,將從該注解標注類所在的包進行掃描,
* 與XML形式的<context:component scan>不同的是 @componentScan沒有Config屬性(true * 就開啟了屬性自動注入的功能,如果是false就是關閉屬性自動注入的功能),因為使用
* @ComponentScan則默認所有的類都進行自動注入,會將所有掃描到的組件注入到IOC容器中
*/
public @interface ComponentScan {
}
@SpringBootConfiguration
- @SpringBootConfiguration 是SpringBoot替代@Configuration的注解,增加了自動找到配置的功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //表示這是一個配置類 通過其間接了解到,@SpringBootApplication也是一個配置類
/**
* 用作Spring的標準@Configuration注解的替代,以便可以自動找到配置
*/
public @interface SpringBootConfiguration {
}
@EnableAutoConfiguration
- @EnableAutoConfiguration注解就是啟動自動配置的關鍵注解,其內部使用了@import注解引入了一個AutoConfigurationImportSelector 自動配置類選擇器
@AutoConfigurationPackage //自動配置所在包注解,通過basePackages指定配置所在的包或者通過basePackageClasses指定基本包類,如果未指定,會默認注冊指定注解類所在的包
//AutoConfigurationImportSelector自動配置選擇器,實作了ImportSelector介面,重寫了selectImports方法,自動配置的具體實作就在其內部進行
//ImportSelector介面作用 :根據給定的選擇條件(通常是一個或多個注解屬性)確定應匯入哪個配置類,
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
? 在其內部重寫了selectImports方法, 通過呼叫getAutoConfigurationEntry()方法根據傳入的注解元資料,獲取到自動配置類的物體,而后從物體中獲取具體的配置資訊,配置資訊在物體內部是一個list
//為方便顯示及理解,省略了該類實作的部分介面和具體的代碼實作,需要了解可進入原始碼查看
public class AutoConfigurationImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//AnnotationMetadata: 配置類的注解元資料,也就是配置類的注解資訊
//呼叫getAutoConfigurationEntry()方法根據傳入的注解資訊,獲取并回傳自動配置類的物體
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
//從配置物體中獲取具體的配置資訊,回傳的是一個list集合,而后通過toStringArray()方法轉存到字串陣列中回傳
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
getAutoConfigurationEntry()
//可以先看下獲取的大致流程,而后進入查看器方法內部的具體實作
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//1. 從注解元資料中獲取注解的相應屬性,將相應屬性存盤到map中回傳
//1.1AnnotationAttributes是一個Map集合,其繼承了LinkedHashMap
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//2. 通過getCandidateConfigurations()方法根據注解元資料和注解的屬性資訊 獲取應該進行自動配置的類名,可以理解為自動配置候選項
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//2.1 通過removeDuplicates()方法對自動配置的類名進行去重處理
configurations = removeDuplicates(configurations);
//3. 根據注解元資料和注解屬性獲取到需排除配置項
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//3.1檢查是否有無效的排除類存在
checkExcludedClasses(configurations, exclusions);
//3.2從自動配置候選項中洗掉需要排除的配置項
configurations.removeAll(exclusions);
//4. 呼叫getConfigurationClassFilter()方法獲取到獲取配置的所有AutoConfigurationImportFilter的實作類(對spring.factories進行過濾的類),呼叫filter方法對組態檔進行篩選,而后回傳需要自動配置的類
configurations = getConfigurationClassFilter().filter(configurations);
//5. 根據spring.factories檔案中的AutoConfigurationImportListener事件監聽器發布并處理監聽事件,最后根據多次過濾、判重回傳配置類合集
fireAutoConfigurationImportEvents(configurations, exclusions);
//6. 創建一個新的配置物體ConfigurationEntry并回傳,包含需要配置項configurations,和被排除配置項exclusions
return new AutoConfigurationEntry(configurations, exclusions);
}
下面了解以下getAutoConfigurationEntry()內部呼叫的方法原始碼
從注解元資料中回傳相應的屬性資訊
-
getAttributes(AnnotationMetadata annotationMetadata)
/** * 從注解元資料中回傳相應的屬性資訊, * param 注解元資料資訊 * return 注解元資料的屬性資訊 其本質是一個Map集合 */ protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { /** * getAnnotationClass() 回傳源注解類 -->EnableAutoConfiguration.class * getAnnotationClass().getName(); 獲取注解類的完全限定類名 */ String name = getAnnotationClass().getName(); /** * metadata.getAnnotationAttributes(String annotationName,boolean classValuesAsString) * 作用: 檢索給定注解的屬性 * @param1 要查找的注解類的完全限定類名 * @param2 是否將類參考轉換為String類名,以便作為回傳Map中的值公開,而不是可能必須首先加載的類參考 * *AnnotationAttributes.fromMap(@Nullable Map<String, Object> map); * 基于給定的集合回傳AnnotationAttributes實體,如果該集合是AnnotationAttributes實體或其子類,它將被強制轉換并立即回傳,而無需創建新實體,否則,將通過將提供的映射傳遞給AnnotationAttributes的map)的建構式來創建新實體,其引數是一個Map型別的注解屬性資料源,也就是attrbuties */ AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)); /** *Assert類 是一個協助驗證引數的斷言實用程式類,詳細使用可以查看其原始碼 * Assert.notNull(@Nullable Object object, Supplier<String> messageSupplier)方法 * 作用 : 判斷物件是不是null, 如果為null,報錯提示 * param1 : 要進行判斷的物件 * param2 : 如果為null,要給予回傳的例外資訊 */ Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()+ " annotated with " + ClassUtils.getShortName(name) + "?"); //回傳注解元資料的屬性資訊map集合 return attributes; }?
獲取應該進行自動配置的類名
-
getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes);
/** *根據注解元資料和注解的屬性資訊 獲取應該進行自動配置的類名,可以理解為自動配置的候選項(初選名單) *param1 元注解資料 *param2 元注解資料的屬性資訊集合 *return List<String> 存盤的資料就是應該繼續寧自動配置的類名 */ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //SpringFactoriesLoader是一個用于框架內部使用的通用工廠加載機制 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; }
對自動配置項進行去重處理
- 1 configurations = removeDuplicates(configurations);對自動配置的類名進行去重處理
//通過removeDuplicates()方法對自動配置的類名進行去重處理
//利用Set集合資料不重復特性,將list集合存盤到LinkedHashSet集合中進行去重處理,而后再將去重的結果存盤到List集合中回傳
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
從自動配置項中篩選被排除配置項
- configurations.removeAll(exclusions);
//從自動配置候選項中篩選需排除配置項
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//創建一個需排除配置項集合excluded
Set<String> excluded = new LinkedHashSet<>();
//從屬性資訊集合中獲取到key為exclude的值,將其存盤到excluded集合中
excluded.addAll(asList(attributes, "exclude"));
//從屬性資訊集合中獲取到key為excludeName的資料,回傳的是一個字串陣列,回傳后將其轉化為List集合,存盤到excluded集合中
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
/**
* getExcludeAutoConfigurationsProperty():
*回傳 spring.autoconfigure.exclude 屬性排除的自動配置
*/
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
-----------------------------------------------------------------------------------------
/*下面方法是上面方法所呼叫的個別方法原始碼,不深究者可以略過*/
-----------------------------------------------------------------------------------------
//attributes.getStringArray("excludeName")
public String[] getStringArray(String attributeName) {
return getRequiredAttribute(attributeName, String[].class);
}
exclude 和excludeName 都是指定某些類在專案啟動時不進行自動配置,其一般在@SpringBootApplication 中進行配置,
檢查是否有無效的排除類存在
- 1 configurations.removeAll(exclusions);
//檢查是否有無效的排除類存在
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
//創建一個用于存盤無效配置項的集合
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
//回圈需排除配置項
for (String exclusion : exclusions) {
//根據類的全限定名判斷該類是否存在且可以被加載,并且 需排除配置項集合是否包含該類
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
//如果存在,且不再需排除配置項的集合中,將其添加到無效配置項集合中
invalidExcludes.add(exclusion);
}
}
//如果無效配置項集合不為空,說明存在無效配置項
if (!invalidExcludes.isEmpty()) {
//處理無效配置項 --> 報錯 IllegalStateException 無效狀態例外
handleInvalidExcludes(invalidExcludes);
}
}
-----------------------------------------------------------------------------------------
/*下面方法是上面方法所呼叫的個別方法原始碼,不深究者可以略過*/
-----------------------------------------------------------------------------------------
/**
*ClassUtils.isPresent() 根據類名稱判斷是否存在并且可以加載,如果類或其依賴項之一不存在或無法 加載回傳false
* param1 className 要檢查的類的名稱
* param2 classLoader 要使用的類加載器(如果為null,表示默認的類加載器)
*/
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
//forName(類名稱,類加載器) 用于替換Class.forName()方法, 并且還回傳所提供名稱的類實體
forName(className, classLoader);
return true;
}
catch (IllegalAccessError err) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
className + "]: " + err.getMessage(), err);
}
catch (Throwable ex) {
// Typically ClassNotFoundException or NoClassDefFoundError...
return false;
}
}
從自動配置項中洗掉需要被排除的配置項
- 2 configurations.removeAll(exclusions);
/**
*從自動配置候選項中洗掉需要排除的配置項
* 集合A.removeAll(集合B);作用就是從集合A資料項中洗掉掉集合B所包含的元素
*/
configurations.removeAll(exclusions);
創建配置類過濾器對配置項進行篩選過濾
- configurations = getConfigurationClassFilter().filter(configurations);
//通過getConfigurationClassFilter()獲取所有AutoConfigurationImportFilter的實作類(對spring.factories進行過濾的類),而后呼叫filter方法對組態檔進行篩選,而后回傳需要自動配置的類
configurations = getConfigurationClassFilter().filter(configurations);
-----------------------------------------------------------------------------------------
/*下面方法是上面方法所呼叫的個別方法原始碼,不深究者可以略過*/
-----------------------------------------------------------------------------------------
//獲取配置類過濾器
private ConfigurationClassFilter getConfigurationClassFilter() {
//this.configurationClassFilter當前類的配置類過濾器是不是為null
if (this.configurationClassFilter == null) {
// 獲取AutoConfigurationImportFilter過濾器的實作類集合
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter); //在監聽器注入是有描述,兩者使用的同一方法
}
//實體化配置類過濾器 ,根據 類加載器和過濾器實作類實體化配置類過濾器
//ConfigurationClassFilter類內部含有類加載器和過濾器實作類集合的屬性
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
//回傳配置類過濾器
return this.configurationClassFilter;
}
//getAutoConfigurationImportFilters(); 獲取AutoConfigurationImportFilter過濾器的實作類集合
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
/**
* List<T> loadFacotries(Class<T> factoryType, @Nullable ClassLoader classLoader)
* 使用給定的類加載器從{"META-INF/spring.factories"}加載并實體化指定過濾器工廠的實作類
* 在結果回傳之前會對結果集進行排序
* param1 表示工廠的介面或者抽象類,-->生成其子類
* param2 當前類的類加載器-->用于加載抽象類的實作類
* return 回傳指定介面或者抽象類的實作類List集合
*/
//回傳AutoConfigurationImportFilter實作類的集合
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
//configurations = getConfigurationClassFilter().filter(configurations);
//過濾 自動配置項
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
//將將配置轉化為字串陣列
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
//回圈過濾條件與配置項進行一一匹配,剔除掉條件不成立的配置項
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;
}
//創建結果集集合
List<String> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
//配置項不為null就添加到配置類中
if (candidate != null) {
result.add(candidate);
}
}
//回傳結果
return result;
}
}
創建配置類監聽器對自動配置進行監聽
- fireAutoConfigurationImportEvents(configurations, exclusions);
//根據spring.factories檔案中的AutoConfigurationImportListener事件監聽器發布并處理監聽事件,最后根據多次過濾、判重回傳配置類合集
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
//從{ "META-INF/spring.factories"}加載并實體化自動配置類監聽器 AutoConfigurationImportListener的實作類集合
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
//如果監聽器不為空的話
if (!listeners.isEmpty())
//創建fireAutoConfigurationImportEvents監聽事件
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
//回圈遍歷 判斷listener是否是 Aware 通過Aware介面 實作對bean各階段的監聽
for (AutoConfigurationImportListener listener : listeners) {
//通過Aware類的實作類對監聽器進行配置 -->解這一模塊,可以重點關注以下Aware介面
invokeAwareMethods(listener);
//進行自動配置的匯入 event 到自動配置時進行的事件-->對自動配置的監聽
listener.onAutoConfigurationImportEvent(event);
}
}
}
//根據Aware類對bean的各階段進行監聽配置
private void invokeAwareMethods(Object instance) {
//判斷監聽器是否是Aware或其實作類
if (instance instanceof Aware) {
if (instance instanceof BeanClassLoaderAware) {
/**
* BeanClassLoaderAware 允許bean知道bean的回呼ClassLoader,即當前bean工廠用來加載bean類的類加載器,這主要是由框架類來實作的,這些框架類必須通過名稱來獲取應用程式類,盡管它們本身可能
從共享類加載器加載的,
* 方法 setBeanClassLoader(ClassLoader classLoader);
* 將bean的類加載器 提供給bean實體的回呼,
* 作用范圍: 在填充普通bean屬性之后,初始化回呼之前呼叫
*/
((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
}
if (instance instanceof BeanFactoryAware) {
/**
* BeanFactoryAware 表示介面知道其擁有的{BeanFactory}的bean實作,
*注意 :大多數的bean都可以通過屬性注入和構造注入接收對bean的參考
*方法 :setBeanFactory(BeanFactory beanFactory)
* 向bean實體提供擁有工廠的回呼,
* 作用范圍: 在填充普通bean屬性之后,初始化回呼之前呼叫,
*/
((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
}
if (instance instanceof EnvironmentAware) {
/**
* EnvironmentAware 表示該介面可以收到其運行環境的通知
*方法 :setEnvironment(Environment environment);
* 設定此組件的運行環境
*/
((EnvironmentAware) instance).setEnvironment(this.environment);
}
if (instance instanceof ResourceLoaderAware) {
/**
* ResourceLoaderAware 介面,該介面可以收到該物件運行的ResourceLoader資源封裝類加載器
*方法 :setResourceLoader(ResourceLoader resourceLoader);
*作用 :設定此物件運行的ResourceLoader,可能是一個ResourcePatternResolver
*作用范圍: 在填充普通bean屬性之后,初始化回呼之前呼叫
*/
((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
}
}
}
創建新的配置物體后回傳SelectImports方法體內
- return new AutoConfigurationEntry(configurations, exclusions);
根據需要配置項和被排除項實體化新的配置物體,并回傳
AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
this.configurations = new ArrayList<>(configurations);
this.exclusions = new HashSet<>(exclusions);
}
將配置物體中的配置資訊轉化為字串陣列回傳,完成注入
//獲取最終要匯入的配置物體
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
//從配置物體中獲取具體的配置資訊,回傳的是一個list集合,而后通過toStringArray()方法轉存到字串陣列中回傳
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
本筆記個人查看原始碼時根據立即理解縮寫,如有錯誤可留言告知,謝謝
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/521810.html
標籤:其他
