Spring Boot ConfigurationProperties
注解能够将Propeties
和加了此注解的类属性绑定。那么它是如何工作的?
构建 ConfigurationPropertiesBindingPostProcessor bean
在Spring Boot
中此注解相关的核心处理逻辑在ConfigurationPropertiesBindingPostProcessor
类,实现了BeanPostProcessor
接口,所以此类必须也要作为bean
,一般按照往常Spring Boot
的做法,会构造一个XxxAutoConfiguration
自动配置类,但是此类的自动配置我怎么都没找到,最后发现此类的注册过程由ConfigurationPropertiesBindingPostProcessorRegistrar
动态注册实现。
ConfigurationPropertiesBindingPostProcessor
通过ConfigurationPropertiesBindingPostProcessorRegistrar
类,将此ConfigurationPropertiesBindingPostProcessor
注册到spring BeanFactory
,注册代码如下
public static final String BINDER_BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class
.getName();
private static final String METADATA_BEAN_NAME = BINDER_BEAN_NAME + ".store";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) {
BeanDefinitionBuilder meta = BeanDefinitionBuilder
.genericBeanDefinition(ConfigurationBeanFactoryMetaData.class);
//获取 ConfigurationPropertiesBindingPostProcessor BeanDefinition
BeanDefinitionBuilder bean = BeanDefinitionBuilder.genericBeanDefinition(
ConfigurationPropertiesBindingPostProcessor.class);
bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME);
//注册
registry.registerBeanDefinition(BINDER_BEAN_NAME, bean.getBeanDefinition());
registry.registerBeanDefinition(METADATA_BEAN_NAME, meta.getBeanDefinition());
}
}
ConfigurationPropertiesBindingPostProcessor 绑定配置
由于ConfigurationPropertiesBindingPostProcessor
类实现了BeanPostProcessor
接口,所以所有的bean
都会被ConfigurationPropertiesBindingPostProcessor
的postProcessBeforeInitialization
和postProcessAfterInitialization
处理。其处理方式如下
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
ConfigurationProperties annotation = AnnotationUtils
.findAnnotation(bean.getClass(), ConfigurationProperties.class);
if (annotation != null) {
postProcessBeforeInitialization(bean, beanName, annotation);
}
annotation = this.beans.findFactoryAnnotation(beanName,
ConfigurationProperties.class);
if (annotation != null) {
postProcessBeforeInitialization(bean, beanName, annotation);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
通过代码可以看到postProcessBeforeInitialization
方法内,会对每个bean
做处理。如果此bean
有ConfigurationProperties
注解,会调用postProcessBeforeInitialization(bean, beanName, annotation)
;
private void postProcessBeforeInitialization(Object bean, String beanName,
ConfigurationProperties annotation) {
Object target = bean;
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
factory.setPropertySources(this.propertySources);
factory.setApplicationContext(this.applicationContext);
factory.setValidator(determineValidator(bean));
// If no explicit conversion service is provided we add one so that (at least)
// comma-separated arrays of convertibles can be bound automatically
factory.setConversionService(this.conversionService == null
? getDefaultConversionService() : this.conversionService);
if (annotation != null) {
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
if (StringUtils.hasLength(annotation.prefix())) {
factory.setTargetName(annotation.prefix());
}
}
try {
factory.bindPropertiesToTarget();
}
catch (Exception ex) {
String targetClass = ClassUtils.getShortName(target.getClass());
throw new BeanCreationException(beanName, "Could not bind properties to "
+ targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
}
}
上面的代码里通过factory.bindPropertiesToTarget();
方法,进行bean
和配置绑定。通过追踪,会调用DataBinder
的doBind
方法,进行绑定
protected void doBind(MutablePropertyValues mpvs) {
checkAllowedFields(mpvs);
checkRequiredFields(mpvs);
applyPropertyValues(mpvs);
}
具体细节就不在深入。在doBinder
方法调用之前,会设置DataBinder
对象的一些配置,比如松绑的,忽略未知字段等配置项。
总结
ConfigurationProperties
注解也并没非常的神秘,其内部核心逻辑也是基于BeanPostProcessor
接口,来处理每个bean
,通过过滤获取标有@ConfigurationProperties
注解的class
,通过environment
获取PropertySources
,绑定属性。其中environment
通过EnvironmentAware
接口获取。