springboot源码解读之@ConfigurationProperties原理

动态注册

动态注册要从BeanDefinition的读取说起。ConfigurationClassParser从声明了@Configuration注解的类中读取BeanDefinition的时候,会解析出ImportBeanDefinitionRegistrar并设置到对应的ConfigurationClass中

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
	Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
	boolean checkForCircularImports) {
     //...
	this.importStack.push(configClass);
	try {
		for (SourceClass candidate : importCandidates) {
			if (candidate.isAssignable(ImportSelector.class)) {
				// Candidate class is an ImportSelector -> delegate to it to determine imports				
			}
			else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
				// Candidate class is an ImportBeanDefinitionRegistrar ->
				// delegate to it to register additional bean definitions
				Class<?> candidateClass = candidate.loadClass();
				ImportBeanDefinitionRegistrar registrar =
						ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
								this.environment, this.resourceLoader, this.registry);
				configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
			}
			else {
				// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
				// process it as an @Configuration class				
			}
		}	
}

ImportBeanDefinitionRegistrar是spring提供的动态注册的扩展点,它与@Configuration和@Import注解配合可以实现组件的动态注册。需要该组件时就注册组件,使用相关的功能;不需要时就可以不注册。使用时只需要一个类似@EnableXXX注解就可以决定是否注册该组件。

在postProcessBeanDefinitionRegistry阶段ImportBeanDefinitionRegistrar注册组件相关的BeanDefinition到BeanFacotry中,该逻辑实现在ConfigurationClassBeanDefinitionReader中

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
	registrars.forEach((registrar, metadata) ->
			registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

@EnableConfigurationProperties注解就通过@Import引入了实现了ImportBeanDefinitionRegistrar扩展点的EnableConfigurationPropertiesRegistrar

@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
	Class<?>[] value() default {};
}

EnableConfigurationPropertiesRegistrar的registerBeanDefinition方法如下所示,这里分别注册两类BeanDefinition,一类是处理ConfigurationProperties相关逻辑的处理类,如负责在initializeBean过程中触发属性绑定的ConfigurationPropertiesBindingPostProcessor,执行属性绑定的ConfigurationPropertiesBinder;令一类是@EnableConfigurationProperties注解中指定的配置类。

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 注册相关逻辑的处理类
	registerInfrastructureBeans(registry);
	ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
    // 注册注解中指定的类
	getTypes(metadata).forEach(beanRegistrar::register);
}

配置注入

上面说到ConfigurationPropertiesBindingPostProcessor注册到了BeanFactory中,它执行时会判断每个bean是不是@ConfigurationProperties标注的类型,如果是则完成配置类的属性绑定。

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
	return bean;
}

一个类是否标注了@ConfigurationProperties则由ConfigurationPropertiesBean来判断

public static ConfigurationPropertiesBean get(ApplicationContext applicationContext, Object bean, String beanName) {
	Method factoryMethod = findFactoryMethod(applicationContext, beanName);
	return create(beanName, bean, bean.getClass(), factoryMethod);
}

private static ConfigurationPropertiesBean create(String name, Object instance, Class<?> type, Method factory) {
	ConfigurationProperties annotation = findAnnotation(instance, type, factory, ConfigurationProperties.class);
	if (annotation == null) {
		return null;
	}
	//...
	return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget);
}

 具体的绑定逻辑比较细节,可以追踪ConfigurationPropertiesBinder的执行了解,这里不做展开。

动态刷新

前一篇文章springboot源码解读之RefreshScope动态刷新配置说明了@RefreshScope注解的类可以实现属性的动态刷新。

回顾一下ContextRefresher重新加载配置的refreshEnvironment方法,在最后的阶段它发布了一个EnvironmentChangeEvent事件。那么这个事件会被谁接收呢?

public synchronized Set<String> refreshEnvironment() {
	Map<String, Object> before = extract(
			this.context.getEnvironment().getPropertySources());
	addConfigFilesToEnvironment();
	Set<String> keys = changes(before,
			extract(this.context.getEnvironment().getPropertySources())).keySet();
	this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
	return keys;
}

答案是ConfigurationPropertiesRebinder,顾名思义它的作用就是完成属性的重新绑定。看到它内部持有一个ConfigurationPropertiesBeans对象。

@Component
@ManagedResource
public class ConfigurationPropertiesRebinder
		implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> {

	private ConfigurationPropertiesBeans beans;
    // ...
}

ConfigurationPropertiesBeans实际上保存了所有@ConfigurationProperties注解的bean,且看它收集相关bean的过程

public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException {
	// 如果同时使用@RefreshScope和@ConfigurationProperties,则rebind阶段不会处理。
	if (isRefreshScoped(beanName)) {
		return bean;
	}
	ConfigurationPropertiesBean propertiesBean = ConfigurationPropertiesBean
			.get(this.applicationContext, bean, beanName);
	if (propertiesBean != null) {
		this.beans.put(beanName, propertiesBean);
	}
	return bean;
}

前面已经展示了ConfigurationPropertiesBean可以判断一个类是否添加了@ConfigurationProperties注解,ConfigurationPropertiesBeans就是在每个Bean创建的时候,判断这个Bean如果添加了@ConfigurationProperties注解则收集过来。

上面代码也同样可以说明如果配置类同时使用@RefreshScope和@ConfigurationProperties,则rebind阶段不会重新绑定。

最后在再来看看重新绑定的过程,实际上就是分别执行了BeanFacory的destroyBean和initializeBean。

public void rebind() {
	this.errors.clear();
	for (String name : this.beans.getBeanNames()) {
		rebind(name);
	}
}

public boolean rebind(String name) {	
	if (this.applicationContext != null) {
		try {
			Object bean = this.applicationContext.getBean(name);			
			if (bean != null) {
				//...
				this.applicationContext.getAutowireCapableBeanFactory()
						.destroyBean(bean);
				this.applicationContext.getAutowireCapableBeanFactory()
						.initializeBean(bean, name);
				return true;
			}
		}
		//...
	}
	return false;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值