动态注册
动态注册要从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;
}