欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

@ConfigurationProperties原理浅析

程序员文章站 2022-07-12 11:23:06
...

背景

   用springboot开发总会涉及Properties的处理,@ConfigurationProperties这个注解就是用来处理配置文件到属性类的映射问题的,用起来挺简单的,但是到底咋回事一直也没去了解过,现在看看到底怎么一回事。

试水

    还是先放上一段代码好说话。

//pom
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter</artifactId>
   <version>2.3.0.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-configuration-processor</artifactId>
   <version>2.3.0.RELEASE</version>
</dependency>
//属性文件
@ConfigurationProperties(prefix = "person")
public class FirstDemoProperties {
    private int age;
    private String name;
    //省略get/set/toString
}
//配置文件,说明本代码基于boot2.3.0构建,所以不用@EnableConfigurationProperties注解
//因为ConfigurationPropertiesAutoConfiguration有这个自动配置类的存在
//另通常属性文件都是给哪个组件使用的,而组件一般在配置类中使用,故做此配置类
@Configuration
public class FirstDemoConfiguration {

    @Bean(name = "firstDemoProperties")
    public FirstDemoProperties firstDemoProperties(){
        return new FirstDemoProperties();
    }

    @Bean(name = "myString")
    public String myString(FirstDemoProperties properties){
        System.out.println(properties.toString());
        return null;
    }
}
//boot启动类
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}
//application.yml
person:
  age: 18
  name: ww

    接下来运行启动类就能看到控制台打印,也就说明配置文件和属性对象映射成功。

    这里要多说一句,spring-boot-configuration-processor这个依赖之所以引入,是要解决属性类编译报错问题,这个做编译时注解处理的,javac提供的功能,具体实现类是ConfigurationMetadataAnnotationProcessor,详细可参考javax.annotation.processing.Processor的API做个自定义注解处理器实现,这里解决@ConfigurationProperties。

处理过程

   这里要说到ConfigurationPropertiesBindingPostProcessor后置处理器,先看下该类层次结构。

@ConfigurationProperties原理浅析

    可以看到该处理器实现了比较重要的 BeanProcessor、InitializingBean、ApplicationContextAware,利用ApplicationContextAware给字段applicationContext赋值,利用InitializingBean给registry和binder赋值、利用BeanPostProcessor来处理属性对象属性值绑定。

    整个绑定过程简单说就是ConfigurationPropertiesBindingPostProcessor将属性绑定委托给ConfigurationPropertiesBinder,ConfigurationPropertiesBinder利用应用上下文(ApplicationContext)的环境(Environment)的(PropertySources->MutablePropertySources),协调其它组件进行属性绑定,{Environment提供属性值集合,针对有@ConfigurationProperties的bean进行属性值绑定},当然详细来说还有ConfigurationPropertiesBinder是如何进行属性绑定的?其实ConfigurationPropertiesBinder就是个协调者,真正进行属性绑定的也不是它,它创建BindHandler、Binder,让Binder根据不同的属性值类型(Value、bean)让DataObjectBinder(ValueObjectBinder、JavaBeanBinder)去进行绑定,最终交给BindHandler来进行绑定,其实看到ConfigurationPropertiesBinder的PropertySources字段的时候,也就大概知道绑定核心原理了,就是到环境中找属性资源给类赋值。

    正常的pojo属性赋值spring提供了依赖注入的方式,@ConfigurationProperties标注的属性类spring提供了可配置方式。那接下来可能还要看看application.yml这样的配置文件是如何映射成spring的Enviroment的?我们知道Environment是profile和propertySource的抽象,这样当我们知道了环境也就知道了,当前**的哪个配置文件、哪些属性资源是可获得的。

   springboot应用构建时会准备环境,准备环境时会用多播器发布一个事件ApplicationEnvironmentPreparedEvent,boot的应用监听器ApplicationListener会监听应用事件,该事件的监听器ConfigFileApplicationListener监听到该事件会让EnvironmentPostProcessor进行相应处理,EnvironmentPostProcessor.Loader去load,具体点就是PropertySourceLoader到默认的DEFAULT_SEARCH_LOCATIONS(classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/)和指定的CONFIG_LOCATION_PROPERTY位置去加载PropertySource到Environment中。

思考

    我们知道springcloud的配置管理,核心点是客户端从服务端取得配置信息,服务端对配置信息进行持久化(以某种方式落磁盘如数据库等等),那么取得的配置客户端作何处理呢,不难猜测应该是要映射到环境中,也就用到了MutablePropertySources,到底猜测的是否正确,可以后续分析一下。