@EnableAutoConfiguration 和 @Import 的原理
@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