本文源代码分析对应
Spring Boot 2.1.3 RELEASE
概述
ConfigurationPropertiesBindingPostProcessor是一个BeanPostProcessor,它通常被框架添加到容器,用于解析bean组件上的注解@ConfigurationProperties,将属性源中的属性设置到bean组件。
ConfigurationPropertiesBindingPostProcessor所在包为:org.springframework.boot.context.properties
源代码解析
1 被引入到容器
在不同的应用中,ConfigurationPropertiesBindingPostProcessor被添加到容器的方式可能有所不同,但它都是作为一个bean被添加到容器中的。在一个Spring Boot应用中,只要使用了注解@EnableConfigurationProperties,就会导致ConfigurationPropertiesBindingPostProcessor被添加到容器。比如在Spring Cloud应用中,自动配置PropertySourceBootstrapConfiguration就使用到了注解@EnableConfigurationProperties。
从@EnableConfigurationProperties到ConfigurationPropertiesBindingPostProcessor被引入,大致的逻辑是这样的 :
1.1 @EnableConfigurationProperties 导入了 EnableConfigurationPropertiesImportSelector
package org.springframework.boot.context.properties;
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
//...
}
1.2 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;
}
// ...
}
1.3 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);
}
}
2. 处理@ConfigurationProperties之前的准备工作
要处理@ConfigurationProperties注解,必须要做一些准备工作。这些准备工作是在bean ConfigurationPropertiesBindingPostProcessor自身创建过程中和初始化时完成的。
2.1 设置应用上下文
ConfigurationPropertiesBindingPostProcessor自身实现了ApplicationContextAware接口,所以它自身在实例化过程中,就会被设置应用上下文属性。最终获取属性源就是基于这里设置的应用上下文。
2.2 准备配置属性绑定工具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);
}
2.3 处理@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;
}
上面的方法主要做两件事情 :
2.3.1 获取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;
}
2.3.2 执行配置属性绑定
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负责。
本文深入剖析SpringBoot中ConfigurationPropertiesBindingPostProcessor的工作原理,包括其如何被引入容器、准备工作及处理@ConfigurationProperties注解的过程。
1152

被折叠的 条评论
为什么被折叠?



