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 ,笔者不再分析 
————————————————
版权声明:本文为CSDN博主「恐龙弟旺仔」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_26323323/article/details/81204284

附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读/书籍书单大全:

(点击右侧 即可打开个人博客内有干货):技术干货小栈
=====>>①【Java大牛带你入门到进阶之路】<<====
=====>>②【算法数据结构+acm大牛带你入门到进阶之路】<<===
=====>>③【数据库大牛带你入门到进阶之路】<<=====
=====>>④【Web前端大牛带你入门到进阶之路】<<====
=====>>⑤【机器学习和python大牛带你入门到进阶之路】<<====
=====>>⑥【架构师大牛带你入门到进阶之路】<<=====
=====>>⑦【C++大牛带你入门到进阶之路】<<====
=====>>⑧【ios大牛带你入门到进阶之路】<<====
=====>>⑨【Web安全大牛带你入门到进阶之路】<<=====
=====>>⑩【Linux和操作系统大牛带你入门到进阶之路】<<=====

天下没有不劳而获的果实,望各位年轻的朋友,想学技术的朋友,在决心扎入技术道路的路上披荆斩棘,把书弄懂了,再去敲代码,把原理弄懂了,再去实践,将会带给你的人生,你的工作,你的未来一个美梦。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值