本文源代码分析对应
Spring Boot 2.1.3 RELEASE
ConfigurationPropertiesBindingPostProcessor
是一个BeanPostProcessor
,它通常被框架添加到容器,用于解析bean
组件上的注解@ConfigurationProperties
,将属性源中的属性设置到bean
组件。
ConfigurationPropertiesBindingPostProcessor
所在包为:org.springframework.boot.context.properties
在不同的应用中,ConfigurationPropertiesBindingPostProcessor
被添加到容器的方式可能有所不同,但它都是作为一个bean
被添加到容器中的。在一个Spring Boot
应用中,只要使用了注解@EnableConfigurationProperties
,就会导致ConfigurationPropertiesBindingPostProcessor
被添加到容器。比如在Spring Cloud
应用中,自动配置PropertySourceBootstrapConfiguration
就使用到了注解@EnableConfigurationProperties
。
从@EnableConfigurationProperties
到ConfigurationPropertiesBindingPostProcessor
被引入,大致的逻辑是这样的 :
@EnableConfigurationProperties
导入了 EnableConfigurationPropertiesImportSelector
package org.springframework.boot.context.properties;
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
//...
}
EnableConfigurationPropertiesImportSelector
导入了ConfigurationPropertiesBindingPostProcessorRegistrar
package org.springframework.boot.context.properties;
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
private static final String[] IMPORTS = {
ConfigurationPropertiesBeanRegistrar.class.getName(),
// 在这里导入了 ConfigurationPropertiesBindingPostProcessorRegistrar
ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
@Override
public String[] selectImports(AnnotationMetadata metadata) {
return IMPORTS;
}
// ...
}
ConfigurationPropertiesBindingPostProcessorRegistrar
导入了ConfigurationPropertiesBindingPostProcessor
package org.springframework.boot.context.properties;
public class ConfigurationPropertiesBindingPostProcessorRegistrar
implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 这里的 if 语句确保ConfigurationPropertiesBindingPostProcessor bean 最多只被注册一次
if (!registry.containsBeanDefinition(
ConfigurationPropertiesBindingPostProcessor.BEAN_NAME)) {
// 将 ConfigurationPropertiesBindingPostProcessor作为一个 bean 注册到容器
registerConfigurationPropertiesBindingPostProcessor(registry);
// 将 ConfigurationBeanFactoryMetadata 作为一个 bean 注册到容器
registerConfigurationBeanFactoryMetadata(registry);
}
}
private void registerConfigurationPropertiesBindingPostProcessor(
BeanDefinitionRegistry registry) {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(ConfigurationPropertiesBindingPostProcessor.class);
// 注意:这是一个基础设施bean
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(
ConfigurationPropertiesBindingPostProcessor.BEAN_NAME, definition);
}
private void registerConfigurationBeanFactoryMetadata(
BeanDefinitionRegistry registry) {
GenericBeanDefinition definition = new GenericBeanDefinition();
// 注意:这是一个基础设施bean
definition.setBeanClass(ConfigurationBeanFactoryMetadata.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(ConfigurationBeanFactoryMetadata.BEAN_NAME,
definition);
}
}
@ConfigurationProperties
之前的准备工作要处理@ConfigurationProperties
注解,必须要做一些准备工作。这些准备工作是在bean
ConfigurationPropertiesBindingPostProcessor
自身创建过程中和初始化时完成的。
ConfigurationPropertiesBindingPostProcessor
自身实现了ApplicationContextAware
接口,所以它自身在实例化过程中,就会被设置应用上下文属性。最终获取属性源就是基于这里设置的应用上下文。
ConfigurationPropertiesBinder
ConfigurationPropertiesBindingPostProcessor
处理@ConfigurationProperties
注解进行属性绑定的具体工作是交给ConfigurationPropertiesBinder
来完成的。所以在处理@ConfigurationProperties
注解前,该工具需要就绪。这一工具的准备工作,是在ConfigurationPropertiesBindingPostProcessor
的初始化方法里面的完成的。
ConfigurationPropertiesBindingPostProcessor
实现了接口InitializingBean
,该接口约定的初始化方法是 :
@Override
public void afterPropertiesSet() throws Exception {
// We can't use constructor injection of the application context because
// it causes eager factory bean initialization
// ConfigurationBeanFactoryMetadata 是容器中记录各个 bean 工厂方法元数据信息的bean
// 这里获取该bean,在配置属性绑定时会有用
this.beanFactoryMetadata = this.applicationContext.getBean(
ConfigurationBeanFactoryMetadata.BEAN_NAME,
ConfigurationBeanFactoryMetadata.class);
// VALIDATOR_BEAN_NAME 常量为 "configurationPropertiesValidator"
// 构建配置属性绑定工具 configurationPropertiesBinder
this.configurationPropertiesBinder = new ConfigurationPropertiesBinder(
this.applicationContext, VALIDATOR_BEAN_NAME);
}
@ConfigurationProperties
注解容器中的每个bean
组件都有可能使用了@ConfigurationProperties
注解,所以ConfigurationPropertiesBindingPostProcessor
对这些注解的处理需要过滤每个bean
。该逻辑是现在ConfigurationPropertiesBindingPostProcessor
的方法postProcessBeforeInitialization
,该方法是ConfigurationPropertiesBindingPostProcessor
所实现接口BeanPostProcessor
约定的方法。它会针对每个bean
在该bean
初始化前调用。该方法的实现如下 :
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// 获取当前正在创建的bean上的注解属性@ConfigurationProperties
ConfigurationProperties annotation = getAnnotation(bean, beanName, ConfigurationProperties.class);
// 如果当前bean使用了注解 @ConfigurationProperties ,则进行配置属性绑定,如果没有,则直接跳过配置属性绑定
if (annotation != null) {
bind(bean, beanName, annotation);
}
return bean;
}
上面的方法主要做两件事情 :
bean
注解@ConfigurationProperties
属性 private <A extends Annotation> A getAnnotation(Object bean, String beanName,Class<A> type) {
// 从bean的工厂元数据中查看是否有指定类型的注解
A annotation = this.beanFactoryMetadata.findFactoryAnnotation(beanName, type);
if (annotation == null) {
// 如果bean的工厂元数据中没有有指定类型的注解
// 则再尝试从bean自身的类上查看是否有指定类型的注解
annotation = AnnotationUtils.findAnnotation(bean.getClass(), type);
}
return annotation;
}
private void bind(Object bean, String beanName, ConfigurationProperties annotation) {
ResolvableType type = getBeanType(bean, beanName);
Validated validated = getAnnotation(bean, beanName, Validated.class);
Annotation[] annotations = (validated != null)
? new Annotation[] { annotation, validated }
: new Annotation[] { annotation };
Bindable<?> target = Bindable.of(type).withExistingValue(bean).withAnnotations(annotations);
try {
// 使用配置属性绑定工具 configurationPropertiesBinder 进行属性绑定,
// 目标bean是 bean,已经被包装成 target
this.configurationPropertiesBinder.bind(target);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(beanName, bean, annotation,
ex);
}
}
private ResolvableType getBeanType(Object bean, String beanName) {
Method factoryMethod = this.beanFactoryMetadata.findFactoryMethod(beanName);
if (factoryMethod != null) {
return ResolvableType.forMethodReturnType(factoryMethod);
}
return ResolvableType.forClass(bean.getClass());
}
从以上分析可以看出,ConfigurationPropertiesBindingPostProcessor
主要负责以下任务 :
ConfigurationPropertiesBinder
;@ConfigurationProperties
的bean
组件,使用配置属性绑定工具ConfigurationPropertiesBinder
对它们进行配置属性绑定。
注意 : 配置属性的绑定细节由
ConfigurationPropertiesBinder
负责,而不是由ConfigurationPropertiesBindingPostProcessor
负责。
通过@ConfigurationProperties使用外部配置填充Bean属性的几种方法