博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
记一次解决Nacos-SpringBoot不支持@ConditionalOnProperty
阅读量:5826 次
发布时间:2019-06-18

本文共 5286 字,大约阅读时间需要 17 分钟。

错误介绍

错误原因分析

在使用Nacos-Config-SpringBoot时,有使用者反馈说无法支持@ConditionalOnProperty注解,经过研究Nacos-Config-SpringBootNacos-Spring-Context源码以及调试跟踪SpringBoot的初始化流程,最终发现问题所在——@ConditionalOnProperty的解析时间与Nacos-Spring-Context相关Bean的注册以及工作时间存在先后问题(其本质原因就是Bean的加载顺序)

@ConditionalOnProperty解析时间

要想知道@ConditionalOnProperty注解何时被Spring解析,首先要看另外一个类——ConfigurationClassPostProcessor,这个类实现了BeanFactoryPostProcessor接口,因此他可以在Springbean创建之前,对bean进行修改,即Spring允许BeanFactoryPostProcessor在容器实例化任何其他bean之前读取配置元数据,并根据其进行修改,就比如@ConditionalOnProperty注解常用来根据配置文件的相关配置控制是否需要创建相应的bean

核心代码

ConfigurationClassPostProcessor执行、装载时机

refreshContext(context);@Overrideprotected final void refreshBeanFactory() throws BeansException {		...				loadBeanDefinitions(beanFactory);				synchronized (this.beanFactoryMonitor) {						this.beanFactory = beanFactory;				}		}		catch (IOException ex) {			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);		}}复制代码

处理@Configuration等注解的处理代码

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {    List
configCandidates = new ArrayList(); String[] candidateNames = registry.getBeanDefinitionNames(); String[] var4 = candidateNames; int var5 = candidateNames.length; ... do { parser.parse(candidates); parser.validate(); Set
configClasses = new LinkedHashSet(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } ...}复制代码

在代码parser.getConfigurationClasses()执行后,会根据Environment中的配置元数据以及@ConditionalOnProperty中的设置,对bean进行过滤操作,不满条件的bean不会被转载进Spring Container

people.enable=false时

people.enable=true时

(提醒:之所以这里nacos的配置能够与SpringBoot@ConditionalOnProperty,是因为我这进行了修改,初步解决了此问题)

Nacos-Spring-Context中相关的bean,都是在这之后才被解析、装载进入Spring Container的,为什么这么说?这里直接拉出官方对于ConfigurationClassPostProcessor的注释

* {@link BeanFactoryPostProcessor} used for bootstrapping processing of* {@link Configuration @Configuration} classes.** 

Registered by default when using {@code

} or* {@code
}. Otherwise, may be declared manually as* with any other BeanFactoryPostProcessor.**

This post processor is priority-ordered as it is important that any* {@link Bean} methods declared in {@code @Configuration} classes have* their corresponding bean definitions registered before any other* {@link BeanFactoryPostProcessor} executes.复制代码

大致意思就是,ConfigurationClassPostProcessor的优先级是所有BeanFactoryPostProcessor实现中最高的,他必须在其他的BeanFactoryPostProcessor之前执行,因为其他的BeanFactoryPostProcessor需要ConfigurationClassPostProcessor进行解析装载进Spring Container

解决方案

清楚该ISSUE为什么会出现的原因之后,那么相应的解决方案就很快的出来了。之前说到ConfigurationClassPostProcessor它会去解析@ConditionalOnProperty,并且它的执行先于其他的BeanFactoryPostProcessor实现;那么,还有什么比它先执行呢?

ApplicationContextInitializer

prepareContext(context, environment, listeners, applicationArguments, printedBanner);复制代码

可以看出,ApplicationContextInitializer是在prepareContext执行的,而ConfigurationClassPostProcessor是在refreshContext执行的,因此,我们只需要将配置拉取的代码提前到ApplicationContextInitializer中即可

@Overridepublic void initialize(ConfigurableApplicationContext context) {    environment = context.getEnvironment();    if (isEnable()) {        CompositePropertySource compositePropertySource = new CompositePropertySource(NacosConfigConstants.NACOS_BOOTSTRAP_PROPERTY_APPLICATION);        CacheableEventPublishingNacosServiceFactory singleton = CacheableEventPublishingNacosServiceFactory.getSingleton();        singleton.setApplicationContext(context);        String[] dataIds;        String[] groupIds;        String[] namespaces;        try {            dataIds = environment.getProperty(NacosConfigConstants.NACOS_CONFIG_DATA_ID, String[].class, new String[]{});            groupIds = environment.getProperty(NacosConfigConstants.NACOS_CONFIG_GROUP_ID, String[].class, new String[dataIds.length]);            namespaces = environment.getProperty(NacosProperties.NAMESPACE, String[].class, new String[dataIds.length]);            for (int i = 0; i < dataIds.length; i ++) {                Properties buildInfo = properties(namespaces[i]);                ConfigService configService = singleton.createConfigService(buildInfo);                String group = StringUtils.isEmpty(groupIds[i]) ? Constants.DEFAULT_GROUP : groupIds[i];                String config = configService.getConfig(dataIds[i], group, 1000);                if (config == null) {                    logger.error("nacos-config-spring-boot : get config failed");                    continue;                }                String name = buildDefaultPropertySourceName(dataIds[i], groupIds[i], buildInfo);                NacosPropertySource nacosPropertySource = new NacosPropertySource(name, config);                compositePropertySource.addPropertySource(nacosPropertySource);            }            environment.getPropertySources().addFirst(compositePropertySource);        } catch (NacosException e) {            logger.error(e.getErrMsg());        }    }}复制代码

转载于:https://juejin.im/post/5cea3c7df265da1b7e1013d0

你可能感兴趣的文章
读书:为了那个美妙的咔哒声
查看>>
我从过去八个月的AI公司面试中学到了什么?
查看>>
jQuery实践小结
查看>>
深入探究Immutable.js的实现机制(一)
查看>>
jsp改造之sitemesh注意事项
查看>>
iOS底层原理总结 - 探寻block的本质(二)
查看>>
智能硬件的时代,嵌入式是否已经日薄西山
查看>>
单点登录(SSO)看这一篇就够了
查看>>
SpringBoot-Shiro使用
查看>>
分布式理论:CAP是三选二吗?
查看>>
iOS 9.0之后NSString encode方法替换
查看>>
解决 ThinkPHP5 无法接收 客户端 Post 传递的 Json 参数
查看>>
ASMFD (ASM Filter Driver) Support on OS Platforms (Certification Matrix). (文档 ID 2034681.1)
查看>>
gitlab 账号注册及修改资料
查看>>
pxssh交换机自动刷限速模板
查看>>
CRM Transaction处理中的权限控制
查看>>
在PL/SQL中获取操作系统环境变量
查看>>
[转]linux创建链接文件的两种方法
查看>>
python ipaddress模块使用
查看>>
统计文件里面某个字符串出现次数
查看>>