【Spring Boot源碼分析】@EnableAutoConfiguration註解(一)@AutoConfigurationImportSelector註解的處理

  • 2019 年 10 月 8 日
  • 筆記

Java及Spring Boot新手,首次嘗試源碼分析,歡迎指正!

 

一、概述

@EnableAutoConfiguration註解是Spring Boot中配置自動裝載的總開關。本文將從@EnableAutoConfiguration入手,嘗試通過源碼分析增強對Spring Boot的理解。
 
所用版本:Spring Boot 2.2.0.M5 + Spring Framework 5.2.0.RC1
 

1. 功能的實現:(Spring Boot部分)

boot.autoconfigure.EnableAutoConfiguration註解
  -> @Import了一個AutoConfigurationImportSelector實例
  -> AutoConfigurationImportSelector類(implement ImportSelector),實現了selectImports() 方法,用來篩選被@Import的Configuration類(減去exclude等)

 

2. 介面的調度:(Spring部分)

context.annotation.ConfigurationClassParser類的parse() 方法
    -> 調用對應不同BeanDefinition類型的parse() 方法
    |   -> 調用context.annotation.ConfigurationClassParser.doProcessConfigurationClass()方法處理ConfigurationClass
    |       -> 調用processImports()方法來處理所有@Import註解
    |           -> 遍歷每個@Import標籤,生成被注入的ImportSelector子類的實例
    |               -> 對於普通ImportSelector,調用其selectImport()方法,篩掉exclude的,再嵌套processImports(),對需要被@Import的類的@Import註解進行處理
    |               -> 對於DefferedImportSelector,只加入deferredImportSelectors列表中
    -> 對defferedImportImportSelectors調用相應handler的process()方法進行處理
        -> 對DefferedImportImportSelector調用processImports()

 

3. 介面在框架中的位置:(其中一條路徑,由頂向下)

【Spring Boot部分】
boot.SpringApplication.main() 或 ApplicationStarter.main()
boot.SpringApplication.run()
boot.SpringApplication.refreshContext()
boot.SpringApplication.refresh()
boot.web.servlet.context.ServletWebServerApplicationContext.refresh()
【Spring部分】
context.support.AbstractApplicationContext.refresh()
context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors()
context.support.PostPreprocessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory()
context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions()
context.annotation.ConfigurationClassParser.parse()  (正是上一小結所述介面)

 

二、源碼細節

(SpringBoot) boot.autoconfigure.EnableAutoConfiguration註解

@Target(ElementType.TYPE)  @Retention(RetentionPolicy.RUNTIME)  @Documented  @Inherited  @AutoConfigurationPackage  @Import(AutoConfigurationImportSelector.class) // 載入selector,識別AutoConfigutaion類並import  public @interface EnableAutoConfiguration {  	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";  	// @interface的參數,以方法形式聲明  	Class<?>[] exclude() default {};  	String[] excludeName() default {};  }  

 
@EnableAutoConfiguration註解有兩個參數和一個屬性變數。
注入了一個AutoConfigurationImportSelector類實例,看起來應該是用於篩選AutoConfiguration的Import的。
@AutoConfigurationPackage待以後另行分析。

 

(SpringBoot) boot.autoconfigure.AutoConfigurationImportSelector類

    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,  		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {      //......      @Override  	public String[] selectImports(AnnotationMetadata annotationMetadata) {  		// 如果AutoConfiguration沒開,返回{}  		if (!isEnabled(annotationMetadata)) {  			return NO_IMPORTS;  		}  		// 將spring-autoconfigure-metadata.properties的鍵值對配置載入到PropertiesAutoConfigurationMetadata對象中並返回  		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader  				.loadMetadata(this.beanClassLoader);          // 基於各種配置計算需要import的configuration和exclusion  		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,  				annotationMetadata);  		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());  	}        // 判斷AudoConfiguration是否開啟  	protected boolean isEnabled(AnnotationMetadata metadata) {  		if (getClass() == AutoConfigurationImportSelector.class) {  			// 如果配置文件中有"spring.boot.enableautoconfiguration",返回該欄位的值;否則返回true  			return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);  		}  		return true;  	}        protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,  			AnnotationMetadata annotationMetadata) {  		if (!isEnabled(annotationMetadata)) {  			return EMPTY_ENTRY;  		}  		// 獲取註解的屬性值  		AnnotationAttributes attributes = getAttributes(annotationMetadata);  		// 從META-INF/spring.factories文件中獲取EnableAutoConfiguration所對應的configurations  		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);  		// 去重,List轉Set再轉List  		configurations = removeDuplicates(configurations);  		// 從註解的exclude/excludeName屬性中獲取排除項  		Set<String> exclusions = getExclusions(annotationMetadata, attributes);  		// 對於不屬於AutoConfiguration的exclude報錯  		checkExcludedClasses(configurations, exclusions);  		// 從configurations去除exclusions  		configurations.removeAll(exclusions);  		// 由所有AutoConfigurationImportFilter類的實例再進行一次篩選,去  		configurations = filter(configurations, autoConfigurationMetadata);  		// 把AutoConfigurationImportEvent綁定在所有AutoConfigurationImportListener子類實例上  		fireAutoConfigurationImportEvents(configurations, exclusions);  		// 返回(configurations, exclusions)組  		return new AutoConfigurationEntry(configurations, exclusions);  	}      // ......  }  

 

可見selectImports()是AutoConfigurationImportSelector的核心函數,其核心功能就是獲取spring.factories中EnableAutoConfiguration所對應的Configuration類列表,由@EnableAutoConfiguration註解中的exclude/excludeName參數篩選一遍,再由AutoConfigurationImportFilter類所有實例篩選一遍,得到最終的用於Import的configuration和exclusion。
該函數是被誰調用的呢?在org.springframework.context.annotation.ConfigurationClassParser類中被processImports()調用,而processImports()函數被doProcessConfigurationClass()調用。下面從doProcessConfigurationClass() 看起。
 
 

(Spring) context.annotation.ConfigurationClassParser.doProcessConfigurationClass()方法

其中,configClass是一個ConfigurationClass實例,記錄了bean name(返回的bean名)和meta data(配置數據);sourceClass是簡單封裝後的有註解的類,主要方便對類的註解的使用,初始值是封裝過的configClass。

doProcessConfigurationClass() 對ConfigurationClass進行了各種配置,包括process @ComponentScan, process @Bean, process @Import等等。如果該SourceClass有父類,返回父類,否則返回null。

 

        @Nullable  	protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)  			throws IOException {  		// ......  		// Process any @Import annotations  		processImports(configClass, sourceClass, getImports(sourceClass), true);    		// ......  		// Process superclass, if any  		if (sourceClass.getMetadata().hasSuperClass()) {  			String superclass = sourceClass.getMetadata().getSuperClassName();  			if (superclass != null && !superclass.startsWith("java") &&  					!this.knownSuperclasses.containsKey(superclass)) {  				this.knownSuperclasses.put(superclass, configClass);  				// Superclass found, return its annotation metadata and recurse  				return sourceClass.getSuperClass();  			}  		}    		// No superclass -> processing is complete  		return null;  	}  

 

其中,processImports()方法的第三個參數中,getImports()方法嵌套的遍歷了sourceClass的註解,搜集所有@Import註解的值,即被Import的類名集合。

 

	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {  		Set<SourceClass> imports = new LinkedHashSet<>();  		Set<SourceClass> visited = new LinkedHashSet<>();  		collectImports(sourceClass, imports, visited);  		return imports;  	}    	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)  			throws IOException {          // 如果是頭一次添加  		if (visited.add(sourceClass)) {  			for (SourceClass annotation : sourceClass.getAnnotations()) {  				String annName = annotation.getMetadata().getClassName();                  // 當前註解的類名是否是Import  				if (!annName.equals(Import.class.getName())) {                      // 嵌套遍歷被Import的類  					collectImports(annotation, imports, visited);  				}  			}              // 增加Import註解的值,即被Import的類名  			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));  		}  	}  

 

(Spring) context.annotation.ConfigurationClassParser.processImports()方法

processImports() 的第一個參數configClass,是上層函數processConfigurationClass()的唯一參數,即被處理的Configuration類。第二個參數currentSourceClass是configClass的SourceClass類封裝。第三個參數是嵌套遍歷出的所有需要被Import的類。第四個參數指定是否檢查循環import。

	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,  			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {    		// ......  				for (SourceClass candidate : importCandidates) {                      // 如果candidate(即被@Import的類)是ImportSelector的子類  					if (candidate.isAssignable(ImportSelector.class)) {  						// Candidate class is an ImportSelector -> delegate to it to determine imports  						Class<?> candidateClass = candidate.loadClass();                          // 生成candidate class(一個ImportSelector子類)的實例  						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);                          // 將ImportSelector子類實例掛載為對應功能的Aware類(用於消息通知?)  						ParserStrategyUtils.invokeAwareMethods(  								selector, this.environment, this.resourceLoader, this.registry);                          // DeferredImportSelector是一種特殊的ImportSelector,這裡單獨處理  						if (selector instanceof DeferredImportSelector) {                              // 掛到deferredImportSelectors列表上  							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);  						}  						else {                              // 篩選符合要求的@Import的名字  							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());                              // 轉換成名字對應的類的集合  							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);                              // 嵌套判斷被@Import的類是不是還有@Import註解  							processImports(configClass, currentSourceClass, importSourceClasses, false);  						}  					}  					// ......  				}  		// ......  	}  

 

(Spring) context.annotation.ConfigurationClassParser.DefferedImportSelectorHandler私有類

deferredImportSelectors已被初始化為ArrayList<>(),因此全部走else分支。

private class DeferredImportSelectorHandler {  		@Nullable  		private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();    		public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {  			DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(  					configClass, importSelector); // 一個封裝後的pair              // deferredImportSelectors被初始化為ArrayList<>(),所以if分支永遠不會執行到?  			if (this.deferredImportSelectors == null) {  				DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();  				handler.register(holder); // 註冊到DeferredImportSelectorGroupingHandler.configurationClasses中  				handler.processGroupImports(); // -> processImports()  			}  			else {                  // 所有的DeferredImportSelector類實例都掛到deferredImportSelectors列表上  				this.deferredImportSelectors.add(holder);  			}  		}          // ......  }  

  

那麼deferredImportSelectors上掛載的DefferedImportSelector類是何時處理的呢?
doProcessConfigurationClass() 方法被processConfigurationClass() 調用,而processConfigurationClass() 被parse() 調用。可以看到在處理完所有的普通ImportSelector類後,即嵌套載入需要的被Import的類的實例之後,再統一處理DefferedImportSelector類。
 

(Spring) context.annotation.ConfigurationClassParser.processConfigurationClass() 方法

    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {          // ...            // Recursively process the configuration class and its superclass hierarchy.          SourceClass sourceClass = asSourceClass(configClass);          do {              // 如果sourceClass有父類會返回父類,否則返回null              sourceClass = doProcessConfigurationClass(configClass, sourceClass);          }          while (sourceClass != null);            this.configurationClasses.put(configClass, configClass);      }  

  

(Spring) context.annotation.ConfigurationClassParser.parse() 方法

        public void parse(Set<BeanDefinitionHolder> configCandidates) {      	// 處理所有的ImportSelector類,其中DeferredImportSelector類只掛在deferredImportSelectorHandler列表上不處理,其他均處理,即嵌套遍歷被Import的類  		for (BeanDefinitionHolder holder : configCandidates) {  			BeanDefinition bd = holder.getBeanDefinition();  			try {  				if (bd instanceof AnnotatedBeanDefinition) {  					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());  				}  				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {  					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());  				}  				else {  					parse(bd.getBeanClassName(), holder.getBeanName());  				}  			}  			catch (BeanDefinitionStoreException ex) {  				throw ex;  			}  			catch (Throwable ex) {  				throw new BeanDefinitionStoreException(  						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);  			}  		}        	// 處理deferredImportSelectorHandler上掛著的DeferredImportSelector類  		this.deferredImportSelectorHandler.process();  	}    	protected final void parse(@Nullable String className, String beanName) throws IOException {  		Assert.notNull(className, "No bean class name for configuration class bean definition");  		MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);  		processConfigurationClass(new ConfigurationClass(reader, beanName));  	}    	protected final void parse(Class<?> clazz, String beanName) throws IOException {  		processConfigurationClass(new ConfigurationClass(clazz, beanName));  	}    	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {  		processConfigurationClass(new ConfigurationClass(metadata, beanName));  	}  

  

再來看看DeferredImportSelector實例是如何被處理的。

 

(Spring) context.annotation.ConfigurationClassParser.DefferedImportSelectorHandler私有類

和普通ConfigurationClass一樣,DefferedImportSelector最後也是先註冊到列表中,再依次嵌套處理,只不過在import前多了一個根據order排序。

	// 對@Order(...)進行排序  	private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =  			(o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getImportSelector(), o2.getImportSelector());    	private class DeferredImportSelectorHandler {      	// ...      	public void process() {  			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;  			this.deferredImportSelectors = null;  			try {  				if (deferredImports != null) {  					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();  					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);                      // 添加到handler的configurationClasses列表中  					deferredImports.forEach(handler::register);                      // 對handler中每個grouping的每個configClass,調用processImports()  					handler.processGroupImports();  				}  			}  			finally {  				this.deferredImportSelectors = new ArrayList<>();  			}  		}  	}  

 

DONE.