前言:我们在做自定义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获取属性的值,这段代码看上去没有什么问题,调试一下
发现map类型的data居然是空的,Evaluate一下,看到app-config.data居然是null
由此可以看出在使用environment.getProperty时,需要明确到具体的属性值。我们知道,如果在一个普通的bean里注入AppConfig,在bean实例化完成后是可以拿到值的,如图:
那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();
}
}
调试看一下:
此时我们已经得到了正确的值。以上就是本篇的全部内容。