1. 程式人生 > >Spring Boot 自動配置之@EnableAutoConfiguration

Spring Boot 自動配置之@EnableAutoConfiguration

浪費了“黃金五年”的Java程式設計師,還有救嗎? >>>   

Spring Boot 啟動類上一個 @SpringBootApplication 註解是

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

三個註解組成的一個複合註解。其中 @SpringBootConfiguration 其實也是和@Configuration 註解組成的一個組合註解,功能也是和 @Configuration 相同;@ComponentScan 這個註解是標註需要掃描的包。今天著重說一下 @EnableAutoConfiguration 註解。


其實 @EnableAutoConfiguration 註解也和其它 @Enable* 註解一脈相乘的,簡單說一下就是藉助 @Import 的支援,收集和註冊特定場景相關的Bean的定義:比如:@EnableAspectJAutoProxy 就是通過@Import 註解動態的將Bean註冊到 SpringIoc 容器中,而@EnableAutoConfiguration 是藉助 @Import 把所有符合條件的 Bean 載入到 SpringIoc 容器中。

參看上一篇文章 Spring Boot 自動配置之@Enable*與@Import註解

1、@EnableAutoConfiguration 也是一個組合註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	Class<?>[] exclude() default {};
	
	String[] excludeName() default {};

}

其中最重要的是 @Import(AutoConfigurationImportSelector.class)註解。藉助AutoConfigurationImportSelector ,@EnableAutoConfiguration 幫助Spring Boot 應用將所有符合條件的 @Configuration 配置載入到當前IoC容器中。而最主要的還是藉助於 Spring 框架一的一個工具類:SpringFactoriesLoader 將 META-INF/spring.factories載入配置,spring.factories 檔案是一個典型的properties配置檔案,配置的格式仍然是Key = Value 的形式,中不過 Key 和 Value 都是Java的完整類名。比如:org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.jpa.repository.support.JpaRepositoryFactory

2、AutoConfigurationImportSelector 原始碼

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader); //1
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata); //2
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

  • 1.獲取註解資訊
  • 2.獲取所有配置列表

其中 AutoConfigurationMetadataLoader.loadMetadata方法原始碼

public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
		return loadMetadata(classLoader, PATH);
	}

	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
					: ClassLoader.getSystemResources(path);
			Properties properties = new Properties();
			while (urls.hasMoreElements()) {
				properties.putAll(PropertiesLoaderUtils
						.loadProperties(new UrlResource(urls.nextElement())));
			}
			return loadMetadata(properties);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException(
					"Unable to load @ConditionalOnClass location [" + path + "]", ex);
		}
	}

getAutoConfigurationEntry 方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//1.
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//2.
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
	    //3.
		configurations = removeDuplicates(configurations);
		//4.
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		//5.
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

  • 1.獲取註解上所有屬性資訊
  • 2.獲取候選配置列表 [核心步驟]
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
    			AnnotationAttributes attributes) {
    		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;
    	}
    
    通過SpringFactoriesLoader.loadFactoryNames獲取配置資訊
  • 3.去重
  • 4.配置exclude 資訊去除不需要的