背景介绍
最近把dubbo的版本从2.7.3升级到2.7.15时 ,遇到一个报错 No application config found or its not a valid config! ,对应的异常栈为:
其中 ,我们使用 Springboot 版本是2.2.0 。
于是手动跟踪调试了下 ,发现某些后置处理器执行时 ,在解决它的依赖时 ,会去创建 dubbo 服务的 bean ,然后就会创建 ApplicationConfig 对象了 。注意这个对象其实只是new了一下 ,所有属性都是默认值 ,并非来源于我们定义的 dubbo 配置文件 。
而我们定义的 dubbo 配置文件 ,是在 ApplicationConfig 中的 addIntoConfigManager 中注入的:
public abstract class AbstractConfig implements Serializable {
...
@PostConstruct
public void addIntoConfigManager() {
ApplicationModel.getConfigManager().addConfig(this);
}
...
}
其中 ,@PostConstruct 注解会由 CommonAnnotationBeanPostProcessor 这个后置处理器(继承自 InitDestroyAnnotationBeanPostProcessor)解析执行 。但很遗憾 ,此时它还没有被执行,因此也就没有把正确的dubbo配置注入进去 ,最终导致dubbo框架在校验时发现无效配置 ,继而报错 。
网上相关讨论及解决方案
关于这类报错,网上也有不少讨论 ,如:
No application config found or its not a valid config!
No registry config found or its not a valid config!
同样的配置 ,2.7.3启动成功 ,2.7.6启动报错
简单总结起来 ,由于dubbo没有一个固定的初始化时机 ,而是与 ReferenceBean 等 dubbo 框架中的 beans 初始化相关 。如果它们被过早初始化 ,导致某些 BeanPostProcessor 尚未被执行 ,就会出现dubbo配置丢失的问题 。
自然 ,dubbo官方也注意到了这个问题 ,于是在3.x版本进行了改造 ,针对该问题做了优化 ,参见Dubbo 3 Spring相关优化 。
但3.x做了大量重构 ,如果我们不想升级,应该怎么办呢?
其实根据上面提到的原因 ,我们可以自己定义一个后置处理器 ,拦截 dubbo 框架的 beans,并手动注入对应的配置 。本质上来说 ,将之前来不及执行的注入代码提到前面去 。这样 ,我们前面提到的「某些后置处理器执行时 ,在解决它的依赖时 ,会去创建 dubbo 服务的 bean ,然后就会创建 ApplicationConfig 对象了」 ,就变成了「某些后置处理器执行时 ,在解决它的依赖时 ,会去创建 dubbo 服务的 bean ,此时会被我们自定义的后置处理器拦截 ,并注入对应的dubbo配置 ,然后就会创建正确的 ApplicationConfig 对象」 。
具体做法为:
step1. 定义一个后置处理器 ,识别到 bean 属于 AbstractConfig 配置后,将其注入:
package com.xxx;
import org.apache.dubbo.config.AbstractConfig;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class DubboBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AbstractConfig) {
AbstractConfig abstractConfig = (AbstractConfig) bean;
ApplicationModel.getConfigManager().addConfig(abstractConfig);
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
step2. 定义一个 Initializer ,并将刚才定义的 DubboBeanPostProcessor 注入:
package com.xxx;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class DubboApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().addBeanPostProcessor(new DubboBeanPostProcessor());
}
}
step3. 把 DubboApplicationContextInitializer 注入到Spring框架中 ,可以采用多种方式 。
方式1. 直接在启动类注入:
@ImportResource(locations = {"classpath:dubbo.xml"})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Launcher extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Launcher.class);
springApplication.addInitializers(new DubboApplicationContextInitializer());
springApplication.run(args);
}
}
方式2. 在 resources 的 META-INF 目录下配置 spring.factories 文件并写入:
org.springframework.context.ApplicationContextInitializer=com.xxx.DubboApplicationContextInitializer
声明:本站所有文章,如无特殊说明或标注 ,均为本站原创发布。任何个人或组织 ,在未征得本站同意时 ,禁止复制 、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。