BeanDefinitionRegistry时期如何获取配置文件属性值

前言:我们在做自定义BeanDefinition时,通常需要获取配置文件属性值,来判断我们的逻辑,但此时,由于在Bean定义时期,我们无法通过属性注入获取值,那么如何获取属性呢?

实现环境变量接口EnvironmentAware

代码如下:

YAML

server:
  port: 10001
  servlet:
    context-path: /
spring:
  application:
    name: demo
app-config:
  name: ${spring.application.name}
  data:
    time-out: 60
    model: single

属性类

package com.hzdsn.config.property;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.sql.DataSourceDefinition;
import java.util.Map;

@Data
@Configuration
@ConfigurationProperties(prefix = "app-config")
public class AppConfig {

    private String name;
    private Map data;
}

Bean注册类

package com.hzdsn.config;

import com.hzdsn.config.property.AppConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class AppConfigRegister implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {

    private BeanDefinitionRegistry registry;

    private Environment environment;

    private AppConfig appConfig;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        this.registry = beanDefinitionRegistry;
        // do some thing
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        AppConfig appConfig = new AppConfig();
        appConfig.setName(environment.getProperty("app-config.name"));
        appConfig.setData(environment.getProperty("app-config.data", Map.class));
        this.appConfig = appConfig;
    }


}

这时,我们通过environment.getProperty获取属性的值,这段代码看上去没有什么问题,调试一下

BeanDefinitionRegistry时期如何获取配置文件属性值_第1张图片

 发现map类型的data居然是空的,Evaluate一下,看到app-config.data居然是null 

BeanDefinitionRegistry时期如何获取配置文件属性值_第2张图片

由此可以看出在使用environment.getProperty时,需要明确到具体的属性值。我们知道,如果在一个普通的bean里注入AppConfig,在bean实例化完成后是可以拿到值的,如图:

BeanDefinitionRegistry时期如何获取配置文件属性值_第3张图片

那Spring是如何把属性值绑定到AppConfig里呢,通过阅读源码得知Spring在Bean定义期间在ConfigurationPropertiesBindingPostProcessor类里定义了一个 ConfigurationPropertiesBindingPostProcessor的Bean

    public static void register(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "Registry must not be null");
        if (!registry.containsBeanDefinition(BEAN_NAME)) {
            BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class, ConfigurationPropertiesBindingPostProcessor::new).getBeanDefinition();
            definition.setRole(2);
            registry.registerBeanDefinition(BEAN_NAME, definition);
        }

        ConfigurationPropertiesBinder.register(registry);
    }

这个Bean里有一ConfigurationPropertiesBinder属性,后续的属性绑定都是通过这个bean去执行的,

    private void bind(ConfigurationPropertiesBean bean) {
        if (bean != null && !this.hasBoundValueObject(bean.getName())) {
            Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '" + bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");

            try {
                this.binder.bind(bean);
            } catch (Exception var3) {
                throw new ConfigurationPropertiesBindException(bean, var3);
            }
        }
    }

但我们此时是在Bean定义期间,显然是无法使用此Bean的,继续深挖,发现实际执行的是用ConfigurationPropertiesBinder类里getBinder() 获取一个Buider去执行的

    private Binder getBinder() {
        if (this.binder == null) {
            this.binder = new Binder(this.getConfigurationPropertySources(), this.getPropertySourcesPlaceholdersResolver(), this.getConversionServices(), this.getPropertyEditorInitializer(), (BindHandler)null, ConfigurationPropertiesBindConstructorProvider.INSTANCE);
        }

        return this.binder;
    }

 那我们能不能直接用这个Binder去绑定呢?答案是肯定的,看一下Binder方法,提供了一个静态的get方法

public static Binder get(Environment environment) {
        return get(environment, (BindHandler)null);
    }

 首先我们需要一个Environment,再看一下它的bind方法,最简单的只要一个key和一个类型。

  public  BindResult bind(String name, Class target) {
        return this.bind(name, Bindable.of(target));
    }

万事倶备,成了,代码如下:

package com.hzdsn.config;

import com.hzdsn.config.property.AppConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

@Component
public class AppConfigRegister implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {

    private BeanDefinitionRegistry registry;

    private Environment environment;

    private AppConfig appConfig;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        this.registry = beanDefinitionRegistry;
        // do some thing
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        bindAppConfig();
    }


   public void bindAppConfig() {
        ConfigurationProperties annotation = AppConfig.class.getAnnotation(ConfigurationProperties.class);
        Assert.notNull(annotation, "can not find annotation ConfigurationProperties");
        this.appConfig = Binder.get(environment).bind(annotation.prefix(), AppConfig.class).get();
    }
}

调试看一下:

BeanDefinitionRegistry时期如何获取配置文件属性值_第4张图片

 

 此时我们已经得到了正确的值。以上就是本篇的全部内容。

你可能感兴趣的:(spring,java,spring,boot)