@EnableAutoConfiguration 的作用是開啟 Spring 應用上下文的自動配置,它會嘗試去猜測和配置我們所需要的 bean。 例如:如果我們的 classpath 中有 tomcat-embedded.jar,那么我們可能需要一個 TomcatServletWebServerFactory 的 bean。
@EnableAutoConfiguration 試圖盡可能的智能化: 1, 當我們的 classpath 中存在某些 jar 或者類時,它會幫助我們自動配置 bean; 2, 當我們定義自己的配置時,自動配置的 bean 將不再加載。(具體是通過 一系列的 @ConditionalOnXxx 來實現的)
我們也可以通過注解中的 exclude() 來手動排除任何不想用的配置。 如果沒有權限訪問到指定排除的類的話,可以使用 excludeName(),或者 spring.autoconfigure.exclude
來指定
當使用 @SpringBootApplication 時,@EnableAutoConfiguration 是自動啟用的。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { ... }
org.springframework.boot.autoconfigure.SpringBootApplication
org.springframework.boot.autoconfigure.EnableAutoConfiguration
這兩個注解都來自於 spring-boot-autoconfigure-xx.RELEASE.jar
@EnableAutoConfiguration 的原理
@Enable 開頭的注解上一般都有 @Import 來指定注解的邏輯實現類。
@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 {}; }
@EnableAutoConfiguration 注解繼承了 @Import,@Import 導入的 AutoConfigurationImportSelector 實現了 ImportSelector 接口。
所以,@EnableAutoConfiguration 的原理與 @Import 的用法密不可分。 其實,@EnableXxx 的奧秘就是 @Import 的使用和原理。
AutoConfigurationImportSelector#selectImports()
調用棧:
1. AutoConfigurationImportSelector#getCandidateConfigurations 1.1 AutoConfigurationImportSelector#getSpringFactoriesLoaderFactoryClass() // 返回 org.springframework.boot.autoconfigure.EnableAutoConfiguration 1.2 SpringFactoriesLoader#loadFactoryNames // 通過 SpringBoot SPI 去加載 spring.factories 中 key=org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值
@Import 的使用和原理
ImportSelector
ImportSelector 接口可以根據給定的條件來決定導入哪些 @Configuration 類。 ImportSelector 可以實現以下任何 Aware 接口,這些 Aware 接口會在 selectImports() 之前被調用:
EnvironmentAware
BeanFactoryAware
BeanClassLoaderAware
ResourceLoaderAware
或者,也可以使用構造函數來注入這些類:
Environment
BeanFactory
ClassLoader
ResourceLoader
/** * 根據 @Configuration 類給定的 AnnotationMetadata 注解元數據信息(具體是指 @EnableXxx 的元數據信息)選擇並返回應該導入的類的名稱。 * @return the class names, or an empty array if none */ String[] selectImports(AnnotationMetadata importingClassMetadata)
方法的解釋很明確,就是根據 selectImports() 方法入參里傳入的 AnnotationMetadata(具體是指 @EnableXxx 注解的元數據信息),selectImports() 會返回需要導入的 @Configuration 類的名稱。
ImportSelector 的處理最終由 ConfigurationClassParser 來處理的,詳細參看 org.springframework.context.annotation.ConfigurationClassParser#processImports()
。
ImportSelector 的實現類通常會與 @Import 以相同的方式進行處理,但是,我們也可以將導入的選擇推遲到所有的 @Configuration 類都處理完畢(詳細信息請參閱 DeferredImportSelector)。
DeferredImportSelector
DeferredImportSelector 是 ImportSelector 的變種,在處理完所有 @Configuration bean 之后運行。當所選導入是 @Conditional 時,這種選擇器特別有用。DeferredImportSelector 的實現類還可以提供一個導入組 getImportGroup(),該組可以跨不同的選擇器提供額外的排序和過濾邏輯。
我想 DeferredImportSelector 延遲處理的原因可能是: 讓所有的 @Configuration 執行完之后,該加載到容器中的 bean 都已經加載了,這時再去加載 DeferredImportSelector 所要選擇導入的 @Configuration 時, @ConitionalOnBean 就可以發揮它的作用了,否則,如果提前加載了 DeferredImportSelector 所要選擇導入的 @Configuration,這時,用戶自定義的 @Configuration 還沒有加載呢,結果框架自動導入的配置就優先加載進去了,所以 @ConitionalOnBean 就起不到想要的效果了。
查看源碼可以發現 ConfigurationClassParser#processImports
對 DeferredImportSelector 有特殊的處理,也就是這個延遲的處理。
通過 ImportBeanDefinitionRegistrar 可以在處理 @Configuration 類時注冊其他 beanDefinition 到容器中。 ImportBeanDefinitionRegistrar 可以實現以下任何 Aware 接口,這些 Aware 接口會在 registerBeanDefinitions() 之前被調用:
EnvironmentAware
BeanFactoryAware
BeanClassLoaderAware
ResourceLoaderAware
或者,也可以使用構造函數來注入這些類:
Environment
BeanFactory
ClassLoader
ResourceLoader
/** * 根據 @Configuration 類給定的 AnnotationMetadata 注解元數據信息(具體是指 @EnableXxx 的元數據信息),注冊所需的 beanDefinition。 */ void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
具體是通過 BeanDefinitionRegistry 來注冊 beanDefinition 的。
用法舉例:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
通過 @Import 來導入一個 ImportBeanDefinitionRegistrar 的實例,ImportBeanDefinitionRegistrar#registerBeanDefinitions() 被調用時可以往容器中注入 beanDefinition。
總結
-
@EnableXxx 注解是用在 @Configuration 標記的類上,用來自動配置和加載一些 @Configuration 類或者 bean 的。 所以,@EnableXxx 一定是與 @Configuration 一起使用的。
-
@EnableXxx 一定會與 @Import 一起使用,通過 @Import 來達到自動配置的目的。
@Import 有 3 種用法,分別是:
-
導入具體的配置類
-
導入 ImportSelector(或者 DeferredImportSelector)
-
導入 ImportBeanDefinitionRegistrar
這 3 種類的處理最終都是由org.springframework.context.annotation.ConfigurationClassParser#processImports()
方法來處理導入的。
實例:
導入具體的配置類 :直接導入配置類
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling { }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { ... }
@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 {}; }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
參考:
https://my.oschina.net/floor/blog/4354771