• Spring Boot @EnableAutoConfiguration注解分析


    在学习使用springboot过程中,我们经常碰到以@Enable开头的注解,其实早在Spring3中就已经出现了类似注解,比如@EnableTransactionManagement、@ EnableWebMvc等,本文以@ EnableAutoConfiguration注解为例跟踪一下源码,分析实现过程。

    @EnableAutoConfiguration注解

    @EnableAutoConfiguration注解主要功能是启用自动装配,将classpath中所有包下的META-INF/spring.factories配置文件中Key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的对应的符合自动配置条件的bean定义加载到Spring容器中。

    查看源码可以看到@EnableAutoConfiguration是一个复合注解,自身也使用了其他注解,其中关键的是@Import(AutoConfigurationImportSelector.class)

    @EnableAutoConfiguration注解实现代码

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

    //是否启用EnableAutoConfiguration注解,可在spring.factories中配置 spring.boot.enableautoconfiguration=true 或者false控制是否启用,默认为true
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    //通过class指定需要排除的自动配置类:
    Class<?>[] exclude() default {};

    //通过类名指定需要排除的自动配置类:
    String[] excludeName() default {};
    }
    查看AutoConfigurationImportSelector.class源码发现这个继承接口ImportSelector,所以截取其中最重要的一个方法
    //实现接口ImportSelector的selectImports方法 ,该方法返回一个类名数组,数组中的类会被spring容器所托管起来@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    //判断是否启用自动配置
    if (!isEnabled(annotationMetadata)) {
    return NO_IMPORTS;
    }
    //加载spring内部自动配置的默认原属性
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    .loadMetadata(this.beanClassLoader);
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //关键加载配置实现:
    // getCandidateConfigurations方法实现加载需要自动配置的类,内部通过使用Spring framework提供的SpringFactoriesLoader类,可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置
    List<String> configurations = getCandidateConfigurations(annotationMetadata,
    attributes);
    //去掉重复配置
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    //检查需要排除的配置
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    //spring.factories加载一个org.springframework.boot.autoconfigure.AutoConfigurationImportFilter过滤器OnClassCondition //通过该过//滤器判断现有系统是否引入了某个组件,如果有则进行相关配置.
    configurations = filter(configurations, autoConfigurationMetadata);
    //处理事件监听,具体监听器参见spring.factories中配置
    fireAutoConfigurationImportEvents(configurations, exclusions);

    //返回最终需要托管的类
    return StringUtils.toStringArray(configurations);
    }
    getCandidateConfigurations实现细节
    //实现加载META-INF/spring.factories配置文件,具体配置文件有什么类容,可以自行查看spring-boot-autoconfig-{version}.jar
    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;
    }

    上面分析了EnableAutoConfiguration的实现过程,下面通过一个示例演示如果从外部项目中读取自动配置

    定义一个外部项目autoconfigbean

    pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.sl.autoconfig</groupId>
    <artifactId>autoconfig-bean</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.7.RELEASE</version>
    </dependency>
    </dependencies>
    </project>
    定义需要自动注入的类
    @Configuration
    public class TestConfig {

    @Bean
    public Configdemo configdemo(){
    return new Configdemo();
    }

    }
    public class Configdemo {

    }
    添加resources/META-INF/spring.factories配置文件
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    configtest.Configdemo
    springboot项目中引用autoconfigbean
    <dependency>
       <groupId>com.sl.autoconfig</groupId>
       <artifactId>autoconfig-bean</artifactId>
       <version>1.0-SNAPSHOT</version>
    </dependency>
    启动springboot项目可以通过System.out.println(context.getBeansOfType(Configdemo.class)) 查看是否已经注入到spring容器

    @import注解

    上面的@EnableAutoConfiguration注解通过@Import(AutoConfigurationImportSelector.class)实现了主要功能, 下面来看一下@import注解的作用

    @import是spring framework提供的一个注解,是通过导入的方式把一个或多个bean或者bean的配置类注入到Spring容器中。

    @import源码

    复制代码
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
    
       /**
        * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
        * or regular component classes to import.
        */
       Class<?>[] value();
    
    }
    复制代码

    ImportSelector接口:selectImports方法返回一个class名称数组,该class会被spring容器所托管起来

    复制代码
    public interface ImportSelector {
    
       /**
        * Select and return the names of which class(es) should be imported based on
        * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
        */
       String[] selectImports(AnnotationMetadata importingClassMetadata);
    
    }
    复制代码

    ImportBeanDefinitionRegistrar接口:registerBeanDefinitions方法的参数有一个BeanDefinitionRegistry,该register可以用来向spring容器中注入bean

    复制代码
    public interface ImportBeanDefinitionRegistrar {
    
       /**
        * Register bean definitions as necessary based on the given annotation metadata of
        * the importing {@code @Configuration} class.
        * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
        * registered here, due to lifecycle constraints related to {@code @Configuration}
        * class processing.
        * @param importingClassMetadata annotation metadata of the importing class
        * @param registry current bean definition registry
        */
       public void registerBeanDefinitions(
             AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
    
    }
    复制代码

    下面通过一个示例简单演示一下Import注解通过{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}三种方式注入
    定义配置类

    复制代码
    // ImportByConfiguration类定义的bean都将注入spring容器
    public class ImportByConfiguration {
    
        @Bean
        public  ImportTestClass createImportTestClass(){
            return new ImportTestClass();
        }
    }
    复制代码

    定义ImportSelector接口实现类,

    复制代码
    //selectImports方法返回一个类数据,该数组中的类将会被spring容器所托管起来
    public class ImportSelectorTest implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            /**
             * xxxx逻辑
             */
    
            return new String[]{
                    "com.sl.springbootdemo.EnableAnnotation.ImportTestClass"
            };
        }
    }
    复制代码

    定义ImportBeanDefinitionRegistrar接口实现类

    复制代码
    public class ImportBeanDefinitionRegistrarTest implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            // new一个RootBeanDefinition
            BeanDefinitionBuilder rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ImportTestClass.class);
            //RootBeanDefinition rootBeanDefinition2 = new RootBeanDefinition(ImportTestClass.class);
            // 注册一个name为importTestClassInstance的bean
            registry.registerBeanDefinition("importTestClassInstance", rootBeanDefinition.getBeanDefinition());
            //registry.registerBeanDefinition("importTestClassInstance",rootBeanDefinition2);
        }
    }
    复制代码

    输出到控制台:

    复制代码
    @Import({ImportByConfiguration.class, //导入bean配置类,则配置类中bean也将注入到spring容器
          ImportSelectorTest.class,   //ImportSelector接口方式
          ImportBeanDefinitionRegistrarTest.class  //ImportBeanDefinitionRegistrar接口方式
    })
    //@ComponentScan
    @SpringBootApplication
    public class SpringbootdemoApplication2 {
    
       public static void main(String[] args) {
          ConfigurableApplicationContext context = SpringApplication.run(SpringbootdemoApplication2.class,args);
          System.out.println(context.getBeansOfType(ImportTestClass.class)); 
       }
    }
    复制代码

    控制台打印信息如下:

    {createImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@2bc0cde,
    
    com.sl.springbootdemo.EnableAnnotation.ImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@ac16e08,
    
    importTestClassInstance=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@210a40d6}

    @Import 注解并不是SpringBoot的功能,Spring 3.0开始就提供了@Import这个注解,并在后续版本中逐渐完善,主要用来支持在Configuration类中引入其它的配置类,包括Configuration类, ImportSelector和ImportBeanDefinitionRegistrar的实现类。

  • 相关阅读:
    mysql增量同步到greenplum
    c笔记06--编译与作用域
    C笔记05-选择顺序结构,关系与相等,优先级和结合性
    C笔记02-C数据类型与数据类型转换
    C笔记01-C简介与补码
    jQuery属性操作之.val()函数
    jQuery属性操作之.attr()
    jQuery笔记: 基本概念与jQuery核心
    笔记: js构造函数与原型
    布尔运算符
  • 原文地址:https://www.cnblogs.com/lanblogs/p/15829933.html
Copyright © 2020-2023  润新知