美文网首页Spring
Spring boot | 属性配置

Spring boot | 属性配置

作者: 不一样的卡梅利多 | 来源:发表于2020-05-25 15:43 被阅读0次

    主要分析 application.x 、@ConfigurationProperties 实现原理。

    application.x,表示:x 表示文件后缀名,可以是 properties,yaml,xml,yml

    application 配置文件
    1、Spring bean 创建流程回顾

    AbstractAutowireCapableBeanFactory

    1、函数doCreateBean->createBeanInstance(beanName, mbd, args)

    1、依据mbd 定义创建 bean instance,
    2、将instance 封装到BeanWrapperImpl 中,为后续对象属性设值做准备。
    3、这步只创建了实例,没有对实例的属性值绑定值。

    2、doCreateBean->populateBean(beanName, mbd, instanceWrapper);

    1、从mbd 中获取属性配置,PropertyValues
    pvs=mbd.getPropertyValues()
    2、依据,autowireByName,autowireByType 为 pvs 添加新的属性值。
    3、applyPropertyValues(),beanWapper 将pvs 设值为对象的属性值。
    4、这步以及获取到配置信息给bean 对象的属性设置值。

    PlaceholderConfigurerSupport implements BeanFactoryPostProcessor
    0、BeanFactoryPostProcessor 是bean 配置信息读取完后,首先执行的方法,可以对注册的bean 信息进行修改。

    1、PlaceholderConfigurerSupport 实现了对bean 定义中 PropertyValues 进行扩展,属性值来源有两个方面。

    1、属性的值来源与环境变量,private Environment environment;
    2、属性的值来源于配置文件

    doProcessProperties(): 属性值与BeanDefinition绑定

      BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);    
                 String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
            BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
    
    // 将属性值设置到BeanDefinition 中
    visitor.visitBeanDefinition(bd);
    

    PlaceholderConfigurerSupport类图参考:


    PlaceholderConfigurerSupport.png
    2、Spring boot 为PlaceholderConfigurerSupport 准备环境变量

    1、SpringApplication#run
    省略不相关代码

    ......
    SpringApplicationRunListeners listeners = getRunListeners(args);
    
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    .....
    
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
    .....
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            listeners.environmentPrepared(environment);
            return environment;
    ...
        }
    

    看到listener,应该是观察者模式了。
    SpringApplicationRunListeners 来源META-INF /spring.fatories:
    在创建SpringApplication 对象时候读取,run方法之前。

    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    

    2、listeners.environmentPrepared(environment)
    基于SimpleApplicationEventMulticaster对SpringApplication里面所有ApplicationListener 进行相同的广播通知,通知的事件不一样。

    public EventPublishingRunListener(SpringApplication application, String[] args) {
            this.application = application;
            this.args = args;
            this.initialMulticaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
            }
        }
    
    
    public void environmentPrepared(ConfigurableEnvironment environment) {
            this.initialMulticaster
                    .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
        }
    

    3、ApplicationListener来源于META-INF /spring.fatories
    初始化读取

    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    org.springframework.boot.context.config.ConfigFileApplicationListener,\
    org.springframework.boot.context.config.DelegatingApplicationListener,\
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.context.logging.LoggingApplicationListener,\
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    

    4、ConfigFileApplicationListener
    配置文件有一个很重要的Listener,ConfigFileApplicationListener。这个类进行了application 属性文件的读取,Profile 判断,以及转换成ConfigurableEnvironment,提供给PlaceholderConfigurerSupport 使用。
    ConfigFileApplicationListener 接受到事件通知

    public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ApplicationEnvironmentPreparedEvent) {
                onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
            }
            if (event instanceof ApplicationPreparedEvent) {
                onApplicationPreparedEvent(event);
            }
        }
    
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
            List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
            postProcessors.add(this);
            AnnotationAwareOrderComparator.sort(postProcessors);
            for (EnvironmentPostProcessor postProcessor : postProcessors) {
                postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
            }
        }
    

    EnvironmentPostProcessor 来源于META-INF /spring.factories 文件
    ,ConfigFileApplicationListener 本身也是一个EnvironmentPostProcessor,也是最核心的一个PostProcessor。postProcessors.add(this);

    org.springframework.boot.env.EnvironmentPostProcessor=\
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
    org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
    org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
    org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
    

    ConfigFileApplicationListener#addPropertySources
    进行application 文件解析。

    protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            RandomValuePropertySource.addToEnvironment(environment);
            new Loader(environment, resourceLoader).load();
        }
    

    new Loader()

    Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    .....
                this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
                        getClass().getClassLoader());
            }
    

    propertySourceLoaders解析xml,properties,yaml 格式,解析器来自META-INF /spring.factories

    # PropertySource Loaders
    org.springframework.boot.env.PropertySourceLoader=\
    org.springframework.boot.env.PropertiesPropertySourceLoader,\
    org.springframework.boot.env.YamlPropertySourceLoader
    
    3、Spring boot 配置PlaceholderConfigurerSupport 注册

    autoconfigure 项目文件META-INF/spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    ...autoconfigure.context.PropertyPlaceholderAutoConfiguration
    
    @Configuration(proxyBeanMethods = false)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    public class PropertyPlaceholderAutoConfiguration {
    
        @Bean
        @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
        public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
            return new PropertySourcesPlaceholderConfigurer();
        }
    
    }
    
    小结:

    ConfigFileApplicationListener 对application.x 文件进行读取,并且转换成环境变量,其中有很多地方对象都有来自于META-INF /spring.factories文件,比如,EnvironmentPostProcessor,PropertySourceLoader。autoconfigure 通过配置PropertySourcesPlaceholderConfigurer bean ,将boot 项目和spring 项目连通。环境变量信息一部分来来自于本身,另一部分来自于属性配置文件。

    ConfigurationProperties 注解配置

    入口:
    autoconfigure 项目文件META-INF/spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    autoconfigure.context.ConfigurationPropertiesAutoConfiguration
    
    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties
    public class ConfigurationPropertiesAutoConfiguration {
    
    }
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(EnableConfigurationPropertiesRegistrar.class)
    public @interface EnableConfigurationProperties {
        String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
        Class<?>[] value() default {};
    }
    

    我们可以看到springboot 中只要注解是@EnableXXXXXX,一般都是用import 注解来导入配置信息的。

    EnableConfigurationPropertiesRegistrar 类主要代码:

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            registerInfrastructureBeans(registry);
            ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
            getTypes(metadata).forEach(beanRegistrar::register);
        }
    
    static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
            ConfigurationPropertiesBindingPostProcessor.register(registry);
            BoundConfigurationProperties.register(registry);
            ConfigurationPropertiesBeanDefinitionValidator.register(registry);
            ConfigurationBeanFactoryMetadata.register(registry);
        }
    

    EnableConfigurationPropertiesRegistrar主要注册了两个信息

    1、注册ConfigurationPropertiesBindingPostProcessor ,对被@ConfigurationProperties注解的类,进行属性值绑定。

    2、@EnableConfigurationProperties(arg),注册arg 的类当成一个普通bean。并且arg类被@ConfigurationProperties注解。因为@ConfigurationProperties注解的类要配合其他注解(@Configuration)才能注册到bean定义中,才能生效,如果使用@EnableConfigurationProperties(arg) ,EnableConfigurationPropertiesRegistrar 会将arg添加到bean 定义中,这样才能被ConfigurationPropertiesBindingPostProcessor 处理。

    ConfigurationPropertiesBindingPostProcessor绑定值:

    BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
        Bindable<?> target = propertiesBean.asBindTarget();
        ConfigurationProperties annotation = propertiesBean.getAnnotation();
        BindHandler bindHandler = getBindHandler(target, annotation);
        return getBinder().bind(annotation.prefix(), target, bindHandler);
    }
    
    
    总结:

    Spring boot 加强了默认属性的配置,并且还约定了配置文件application.x 名称。再一次明确约定大于配置的思想。spring boot通过扩展BeanPostProcessor ,如ConfigurationPropertiesBindingPostProcessor ,添加了@ConfigurationProperties注解的支持。还通过对application.x 文件解析,扩展了环境变量的值。明白属性值设置的原理,可以扩展如何将bean 对象的值,设置成自定义的值。也将有助于理解spring cloud 中属性值的配置 如bootstrap.x。

    Spring 专题

    相关文章

      网友评论

        本文标题:Spring boot | 属性配置

        本文链接:https://www.haomeiwen.com/subject/wceyahtx.html