zoukankan      html  css  js  c++  java
  • SpringBoot之@ConfigurationProperties自动注入成员变量值功能源码解析

    我们在使用SpringBoot开发相关项目时,经常会使用到@ConfigurationProperties注解,这个注解配合application.properties(或yml文件)使用,可以快速的为我们的Bean的成员变量进行赋值,常规用法如下:

    / 创建bean
    @ConfigurationProperties(prefix="person")
    @Data
    public class Person {
        private int id;
        private String name;
        private String addr;
    }
     
    // 在application.properties文件中添加
    person.name=jack
    person.id=11
    person.addr=china

    这样就可以为Person实例自动注入属性

     那么SpringBoot是如何为bean实例注入属性值的呢?

    1.关于自动注入属性值的功能猜想

        SpringBoot会为每一个在配置文件中有匹配项的bean注入属性值,根据之前的 BeanPostProcessor功能可知,我们的Spring容器中应该有一个Bean实现BeanPostProcessor接口,然后在返回每一个bean之前,获取配置文件内容,找到对应属性,然后进行赋值操作

        SpringBoot中确实有这么一个Bean,ConfigurationPropertiesBindingPostProcessor,实现了相关赋值功能,那么这个Bean是如何被注入到容器中的呢,下面亲随笔者一层层分析查找

    2.SpringBoot项目启动之@SpringBootApplication

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

    重要的注解就是@EnableAutoConfiguration,下面看下这个注解

    @SuppressWarnings("deprecation")
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(EnableAutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    

     主要功能就是把EnableAutoConfigurationImportSelector注入到容器中,下面看下这个类

    public class EnableAutoConfigurationImportSelector
            extends AutoConfigurationImportSelector {
     
        @Override
        protected boolean isEnabled(AnnotationMetadata metadata) {
            if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
                return getEnvironment().getProperty(
                        EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
                        true);
            }
            return true;
        }
     
    }
     
    // AutoConfigurationImportSelector
    public class AutoConfigurationImportSelector
            implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
            BeanFactoryAware, EnvironmentAware, Ordered {}

    AutoConfigurationImportSelector这个类实现了DeferredImportSelector接口,可知这个类应该在其selectImport方法中,应该获取了一个bean的列表,然后被这个bean列表的所有bean会被注入到容器中,下面重点分析下这个selectImport方法

        对于@Import ImportSelector等接口注入bean到容器的方式不太明白的同学可以参考下这篇文章 https://blog.csdn.net/qq_26323323/article/details/81201717

    3.AutoConfigurationImportSelector.selectImport(AnnotationMetadata annotationMetadata)

        该方法源码如下:

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            try {
                // 1.获取META-INF/spring-autoconfigure-metadata.properties 路径下所有bean URL
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                        .loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = getAttributes(annotationMetadata);
                
                // 2.重要操作在这里
                List<String> configurations = getCandidateConfigurations(annotationMetadata,
                        attributes);
                configurations = removeDuplicates(configurations);
                configurations = sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = getExclusions(annotationMetadata, attributes);
                checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = filter(configurations, autoConfigurationMetadata);
                fireAutoConfigurationImportEvents(configurations, exclusions);
                
                // 3.经过中间的一系列操作返回configurations
                return configurations.toArray(new String[configurations.size()]);
            }
            catch (IOException ex) {
                throw new IllegalStateException(ex);
            }
        }
     
    // getCandidateConfigurations(annotationMetadata,attributes)
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
                AnnotationAttributes attributes) {
            // 主要就是为了获取META-INF/spring.factories 文件下EnableAutoConfiguration对应的value值,并返回该值
            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;
        }

    通过以上分析可知:AutoConfigurationImportSelector类的主要功能就是为了加载META-INF/spring.factories文件下EnableAutoConfiguration对应的value值,这些value值对应一个个bean,Spring会将这些bean加载到容器中

        我们看下是哪些bean(在spring-boot-autoconfigure-xxx.jar的META-INF/spring.factories中)

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
    org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
    org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
    org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,
    org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,
    org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,
    ...
    org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,
    org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,
    org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,
    org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,
    org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,
    ...
    org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,
    org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,
    org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,
    org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,
    org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,
    org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,
    org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,
    org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,
    ...
    org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

    以上展示了部分类,可知,这些类都会被注入到Spring中,下面我们重点关注ConfigurationPropertiesAutoConfiguration类

    4.ConfigurationPropertiesAutoConfiguration

        1)ConfigurationPropertiesAutoConfiguration源码如下:

    @Configuration
    @EnableConfigurationProperties
    public class ConfigurationPropertiesAutoConfiguration {
     
    }
     
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import({EnableConfigurationPropertiesImportSelector.class})
    public @interface EnableConfigurationProperties {
        Class<?>[] value() default {};
    }

    该类没有其他方法,由其注解@EnableConfigurationProperties可知,整个类主要是为了注入EnableConfigurationPropertiesImportSelector这个类,

        2)EnableConfigurationPropertiesImportSelector源码如下:

    class EnableConfigurationPropertiesImportSelector implements ImportSelector {
        public String[] selectImports(AnnotationMetadata metadata) {
            MultiValueMap attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(),
                    false);
            Object[] type = attributes == null ? null : (Object[]) ((Object[]) attributes.getFirst("value"));
            return type != null && type.length != 0
                    ? new String[]{ConfigurationPropertiesBeanRegistrar.class.getName(),
                            ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()}
                    : new String[]{ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()};
        }
    }

    可知其会返回ConfigurationPropertiesBindingPostProcessorRegistrar类到容器中

        3)ConfigurationPropertiesBindingPostProcessorRegistrar源码如下

    public class ConfigurationPropertiesBindingPostProcessorRegistrar implements ImportBeanDefinitionRegistrar {
        public static final String BINDER_BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class.getName();
        private static final String METADATA_BEAN_NAME;
     
        // 主要的注入方法
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) {
                BeanDefinitionBuilder meta = BeanDefinitionBuilder
                        .genericBeanDefinition(ConfigurationBeanFactoryMetaData.class);
                BeanDefinitionBuilder bean = BeanDefinitionBuilder
                        .genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class);
                bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME);
                
                // 真正的注入功能就是这句话,将ConfigurationPropertiesBindingPostProcessor类注册进来
                registry.registerBeanDefinition(BINDER_BEAN_NAME, bean.getBeanDefinition());
                registry.registerBeanDefinition(METADATA_BEAN_NAME, meta.getBeanDefinition());
            }
     
        }
     
        static {
            METADATA_BEAN_NAME = BINDER_BEAN_NAME + ".store";
        }
    }

     总结:通过以上的一层层分析,我们终于到了ConfigurationPropertiesBindingPostProcessor类,也成功将该类注入到容器中,下面我们就分析下ConfigurationPropertiesBindingPostProcessor是如何将参数注入到bean中
    具体分析过程可参考:https://blog.csdn.net/qq_26000415/article/details/78942494 ,笔者不再分析

  • 相关阅读:
    ORACLE常用SQL优化hint语句
    TestNG 入门教程
    博客迁移
    WebMvcConfigurer
    Nginx 配置
    SpringBoot部署
    MyBatis 动态 SQL
    Spring Boot 验证表单
    Spring Boot session与cookie的使用
    Spirng MVC 重定向传递对象
  • 原文地址:https://www.cnblogs.com/zjm-1/p/14482367.html
Copyright © 2011-2022 走看看