zoukankan      html  css  js  c++  java
  • @EnableAutoConfiguration 和 @Import 的原理

    (version:springboot 2.3.2)

    @EnableAutoConfiguration 的作用


     

    @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 的值

    通过 AutoConfigurationImportSelector#selectImports() 返回的自动配置类,ConfigurationClassParser#processImports() 方法再去加载和处理这些配置类。这就是 @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


     

    通过 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。

    总结


     

    1. @EnableXxx 注解是用在 @Configuration 标记的类上,用来自动配置和加载一些 @Configuration 类或者 bean 的。 所以,@EnableXxx 一定是与 @Configuration 一起使用的。

    2. @EnableXxx 一定会与 @Import 一起使用,通过 @Import 来达到自动配置的目的。

    @Import 有 3 种用法,分别是:

    1. 导入具体的配置类

    2. 导入 ImportSelector(或者 DeferredImportSelector)

    3. 导入 ImportBeanDefinitionRegistrar

    这 3 种类的处理最终都是由org.springframework.context.annotation.ConfigurationClassParser#processImports() 方法来处理导入的。

    实例:

    导入具体的配置类 :直接导入配置类

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(SchedulingConfiguration.class)
    @Documented
    public @interface EnableScheduling {
    }

    导入ImportSelector:通过 ImportSelector#selectImports() 来选择需要导入的配置类

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AsyncConfigurationSelector.class)
    public @interface EnableAsync {
        ...
    }

    导入DeferredImportSelector:主要是使用了 DeferredImportSelector 的延迟导入能力

    @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 {};
    }

    导入ImportBeanDefinitionRegistrar:通过 ImportBeanDefinitionRegistrar 来向容器中注入 bean

    @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

  • 相关阅读:
    2.1.4 现代计算机的设计原则(译)
    2.1.2 指令执行(译)
    计算机体系结构之一体系结构【译】
    2.2.1 比特【译】
    2.1.6 处理器级并行【译】
    jQery Datatables回调函数中文
    彪悍的人生不需要解释
    程序员读书雷达
    打开页面自动打开QQ的javascript代码
    EF大数据插入
  • 原文地址:https://www.cnblogs.com/kevin-yuan/p/13583269.html
Copyright © 2011-2022 走看看