起因
事情的起因是這樣的艇棕,在開發(fā)基于nacos的動態(tài)線程池項目時,一開始選用的nacos-pring-context的版本為:0.2.2-RC1逗爹,版本中@NacosConfigListener注解的dataId與groupId不支持動態(tài)解析咽弦,于是相對其進行改造。
查看其源碼發(fā)現(xiàn)起作用的是NacosConfigListenerMethodProcessor類狐树。于是產(chǎn)生了一個想法重新創(chuàng)建創(chuàng)建一個CustomNacosConfigListenerMethodProcessor類注冊到容器中,將NacosConfigListenerMethodProcessor的BeanDefinition進行清除鸿脓,于是創(chuàng)建了一個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();
}
}
現(xiàn)象
到此以為萬事大吉抑钟,然后去進行測試,突然發(fā)現(xiàn)項目報錯野哭,原因是@Value注解的applicationName字段為null在塔,我把ChangeDefinitionRegistryPostProcessor聲明bean的部分注釋掉,項目就可以正常啟動
探查原因
為什么在@ Configuration注解的類中聲明這個ChangeDefinitionRegistryPostProcessor的bean會導(dǎo)致屬性注入失敗呢虐拓,這里需要提前了解三個知識點
- @value注解的注入原理是什么心俗,在哪個類里面進行的,執(zhí)行時機是什么蓉驹,這個BeanPostPerocessor什么時候加入到BeanPostProcessor處理集合中的
- @Bean注解創(chuàng)建bean的過程是什么,這其中會對DynamicExecutorConfiguration產(chǎn)生什么影響
- ChangeDefinitionRegistryPostProcessor的執(zhí)行時機是什么時候
下面來解決這三個問題
第一個問題:@value注解的注入原理是什么揪利,在哪個類里面進行的态兴,執(zhí)行時機是什么?
@value注解的代碼處理是在AutowiredAnnotationBeanPostProcessor的postProcessProperties方法進行處理的疟位,而方法的執(zhí)行時機是在bean的生命周期中屬性注入節(jié)點執(zhí)行的
那什么時候加入到處理器集合中的呢瞻润?
是在refresh方法的registerBeanPostProcessor(beanFactory)方法中加入處理器集合的
第二個問題:@Bean注解創(chuàng)建bean的過程是什么,這其中會對DynamicExecutorConfiguration產(chǎn)生什么影響甜刻?
我們需要知道@Bean的bean的初始化的過程是利用的反射原理來實現(xiàn)绍撞,也就是說在實例化ChangeDefinitionRegistryPostProcessor這個Bean之前我們需要先創(chuàng)建DynamicExecutorConfiguration這個類的對象,也就是先創(chuàng)建DynamicExecutorConfiguration這個bean
因此這里有一個點需要明白在創(chuàng)建ChangeDefinitionRegistryPostProcessor這個bean之前一定是需要先創(chuàng)建好DynamicExecutorConfiguration這個bean的
第三個問題:ChangeDefinitionRegistryPostProcessor的執(zhí)行時機是什么時候得院?
我們來看ChangeDefinitionRegistryPostProcessor屬于BeanDefinitionRegistryPostProcessor傻铣,因此他的執(zhí)行時機是在invokeBeanFactoryPostProcessor(beanFactory)方法中
需要特別提醒與在第一個問題中AutowiredAnnotationBeanPostProcessor是何時計入的BeanPostProcessor處理集合中的,對祥绞,是在后面的registerBeanPostProcessor(beanFactory)中非洲,這些意味著在執(zhí)行invokeBeanFacotryPostProcessors(beanFacotruy)時鸭限,容器中是沒有AutowiredAnnotationBeanPostProcessor這個處理Bean的
到此你是不是發(fā)現(xiàn)了什么?是的两踏,你猜的很多败京,當(dāng)DynamicExecutorConfiguration中聲明了ChangeDefinitionRegistryPostProcessor這個bean時,在執(zhí)行到invokeBeanFacotryPostProcessors(beanFactory)這個方法時會加載ChangeDefinitionRegistryPostProcessor這個bean梦染,而加載ChangeDefinitionRegistryPostProcessor的bean時赡麦,需要先加載DynamicExecutorConfiguration這個bean,因此在初始化單例bean
DynamicExecutorConfiguration的過程的在屬性注入populateBean()階段由于還沒有AutowiredAnnotationBeanPostProcessor這個bean帕识,導(dǎo)致無法完成@Value的屬性注入泛粹,又因為生成的是單例bean,后續(xù)再使用DynamicExecutorConfiguration這個bean時都是從一級緩存中獲取的渡冻,而這個bean是未完成屬性注入的bean
因此這也解釋了為什么DynamicExecutorConfiguration一旦聲明ChangeDefinitionRegistryPostProcessor這個bean時戚扳,就會導(dǎo)致屬性注入失敗的問題