Spring BeanPostProcessor : ConfigurationPropertiesBindingPostProcessor

本文源代码分析对应 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

@EnableConfigurationPropertiesConfigurationPropertiesBindingPostProcessor被引入,大致的逻辑是这样的 :

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主要负责以下任务 :

  1. 准备配置属性绑定工具ConfigurationPropertiesBinder;
  2. 过滤容器中每个带有注解@ConfigurationPropertiesbean组件,使用配置属性绑定工具ConfigurationPropertiesBinder对它们进行配置属性绑定。

    注意 : 配置属性的绑定细节由ConfigurationPropertiesBinder负责,而不是由ConfigurationPropertiesBindingPostProcessor负责。

相关文章

通过@ConfigurationProperties使用外部配置填充Bean属性的几种方法

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值