SpringBoot自動配置(裝配)流程

源碼分析

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

  1. @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

  1. @SpringBootConfiguration 是SpringBoot替代@Configuration的註解,增加了自動找到配置的功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration   //表示這是一個配置類 通過其間接了解到,@SpringBootApplication也是一個配置類
/**
  * 用作Spring的標準@Configuration註解的替代,以便可以自動找到配置
  */
public @interface SpringBootConfiguration {
}

@EnableAutoConfiguration

  1. @EnableAutoConfiguration註解就是啟動自動配置的關鍵註解,其內部使用了@import註解引入了一個AutoConfigurationImportSelector 自動配置類選擇器
@AutoConfigurationPackage //自動配置所在包註解,通過basePackages指定配置所在的包或者通過basePackageClasses指定基本包類,如果未指定,會默認註冊指定註解類所在的包
//AutoConfigurationImportSelector自動配置選擇器,實現了ImportSelector介面,重寫了selectImports方法,自動配置的具體實現就在其內部進行
//ImportSelector介面作用 :根據給定的選擇條件(通常是一個或多個註解屬性)確定應導入哪個配置類。
@Import(AutoConfigurationImportSelector.class) 
public @interface EnableAutoConfiguration {

}

​ 在其內部重寫了selectImports方法, 通過調用getAutoConfigurationEntry()方法根據傳入的註解元數據,獲取到自動配置類的實體,而後從實體中獲取具體的配置資訊,配置資訊在實體內部是一個list集合,所以將其轉化為String數組後返回。

//為方便顯示及理解,省略了該類實現的部分介面和具體的程式碼實現,需要了解可進入源碼查看 
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()內部調用的方法源碼

從註解元數據中返回相應的屬性資訊

  1. 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;
    	}
    

獲取應該進行自動配置的類名

  1. 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. 1 configurations = removeDuplicates(configurations);對自動配置的類名進行去重處理
//通過removeDuplicates()方法對自動配置的類名進行去重處理
//利用Set集合數據不重複特性,將list集合存儲到LinkedHashSet集合中進行去重處理,而後再將去重的結果存儲到List集合中返回 
protected final <T> List<T> removeDuplicates(List<T> list) {
		return new ArrayList<>(new LinkedHashSet<>(list));
} 

從自動配置項中篩選被排除配置項

  1. 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. 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;
		}
}

從自動配置項中刪除需要被排除的配置項

  1. 2 configurations.removeAll(exclusions);
/**
 *從自動配置候選項中刪除需要排除的配置項  
 * 集合A.removeAll(集合B);作用就是從集合A數據項中刪除掉集合B所包含的元素
 */
configurations.removeAll(exclusions);

創建配置類過濾器對配置項進行篩選過濾

  1. 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;
		}
	}

創建配置類監聽器對自動配置進行監聽

  1. 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方法體內

  1. 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());

本筆記個人查看源碼時根據立即理解縮寫,如有錯誤可留言告知,謝謝