起因
事情的起因是这样的,在开发基于nacos的动态线程池项目时,一开始选用的nacos-pring-context的版本为:0.2.2-RC1,版本中@NacosConfigListener注解的dataId与groupId不支持动态解析,于是相对其进行改造。
查看其源码发现起作用的是NacosConfigListenerMethodProcessor类。于是产生了一个想法重新创建创建一个CustomNacosConfigListenerMethodProcessor类注册到容器中,将NacosConfigListenerMethodProcessor的BeanDefinition进行清除,于是创建了一个ChangeDefinitionRegistryPostProcessor
public class ChangeDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if(registry.containsBeanDefinition(NacosConfigListenerMethodProcessor.BEAN_NAME)){
registry.removeBeanDefinition(NacosConfigListenerMethodProcessor.BEAN_NAME);
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(CustomNacosConfigListenerMethodProcessor.class);
// ROLE_INFRASTRUCTURE
beanDefinitionBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// Register
registry.registerBeanDefinition(NacosConfigListenerMethodProcessor.BEAN_NAME, beanDefinitionBuilder.getBeanDefinition());
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
然后在DynamicExecutorConfiguration中声明该bean
@Configuration
@EnableNacos(globalProperties = @NacosProperties())
public class DynamicExecutorConfiguration implements ApplicationEventPublisherAware {
public static final String DYNAMIC_EXECUTOR_PREFIX = "dynamic.executors";
ApplicationEventPublisher publisher;
@Value("${spring.application.name}")
private String applicationName;
@Bean
ChangeDefinitionRegistryPostProcessor changeDefinitionRegistryPostProcessor(){
return new ChangeDefinitionRegistryPostProcessor();
}
}
现象
到此以为万事大吉,然后去进行测试,突然发现项目报错,原因是@Value注解的applicationName字段为null,我把ChangeDefinitionRegistryPostProcessor声明bean的部分注释掉,项目就可以正常启动
探查原因
为什么在@ Configuration注解的类中声明这个ChangeDefinitionRegistryPostProcessor的bean会导致属性注入失败呢,这里需要提前了解三个知识点
- @value注解的注入原理是什么,在哪个类里面进行的,执行时机是什么,这个BeanPostPerocessor什么时候加入到BeanPostProcessor处理集合中的
- @Bean注解创建bean的过程是什么,这其中会对DynamicExecutorConfiguration产生什么影响
- ChangeDefinitionRegistryPostProcessor的执行时机是什么时候
下面来解决这三个问题
第一个问题:@value注解的注入原理是什么,在哪个类里面进行的,执行时机是什么?
@value注解的代码处理是在AutowiredAnnotationBeanPostProcessor的postProcessProperties方法进行处理的,而方法的执行时机是在bean的生命周期中属性注入节点执行的 image.png
image.png
那什么时候加入到处理器集合中的呢?
是在refresh方法的registerBeanPostProcessor(beanFactory)方法中加入处理器集合的
image.png
第二个问题:@Bean注解创建bean的过程是什么,这其中会对DynamicExecutorConfiguration产生什么影响?
我们需要知道@Bean的bean的初始化的过程是利用的反射原理来实现,也就是说在实例化ChangeDefinitionRegistryPostProcessor这个Bean之前我们需要先创建DynamicExecutorConfiguration这个类的对象,也就是先创建DynamicExecutorConfiguration这个bean
因此这里有一个点需要明白在创建ChangeDefinitionRegistryPostProcessor这个bean之前一定是需要先创建好DynamicExecutorConfiguration这个bean的
第三个问题:ChangeDefinitionRegistryPostProcessor的执行时机是什么时候?
我们来看ChangeDefinitionRegistryPostProcessor属于BeanDefinitionRegistryPostProcessor,因此他的执行时机是在invokeBeanFactoryPostProcessor(beanFactory)方法中 image.png
需要特别提醒与在第一个问题中AutowiredAnnotationBeanPostProcessor是何时计入的BeanPostProcessor处理集合中的,对,是在后面的registerBeanPostProcessor(beanFactory)中,这些意味着在执行invokeBeanFacotryPostProcessors(beanFacotruy)时,容器中是没有AutowiredAnnotationBeanPostProcessor这个处理Bean的
到此你是不是发现了什么?是的,你猜的很多,当DynamicExecutorConfiguration中声明了ChangeDefinitionRegistryPostProcessor这个bean时,在执行到invokeBeanFacotryPostProcessors(beanFactory)这个方法时会加载ChangeDefinitionRegistryPostProcessor这个bean,而加载ChangeDefinitionRegistryPostProcessor的bean时,需要先加载DynamicExecutorConfiguration这个bean,因此在初始化单例bean
DynamicExecutorConfiguration的过程的在属性注入populateBean()阶段由于还没有AutowiredAnnotationBeanPostProcessor这个bean,导致无法完成@Value的属性注入,又因为生成的是单例bean,后续再使用DynamicExecutorConfiguration这个bean时都是从一级缓存中获取的,而这个bean是未完成属性注入的bean
因此这也解释了为什么DynamicExecutorConfiguration一旦声明ChangeDefinitionRegistryPostProcessor这个bean时,就会导致属性注入失败的问题
网友评论