spring-boot中dubbo的配置冲突检测导致dubbo消费者的全局配置不生效
先导片
首先,大概介绍下dubbo的配置冲突检测----DubboConfigBeanDefinitionConflictProcessor,主要是用来在项目启动时候,去检测项目中是否有多个org.apache.dubbo.config.ApplicationConfig配置。
private void resolveUniqueApplicationConfigBean(BeanDefinitionRegistry registry,
ConfigurableListableBeanFactory beanFactory) {
this.environment = beanFactory.getBean(ENVIRONMENT_BEAN_NAME, Environment.class);
//在spring的所有beanDefinitionNames中遍历,找出beanclass类型是org.apache.dubbo.config.ApplicationConfigbeansName
String[] beansNames = beanNamesForTypeIncludingAncestors(beanFactory, ApplicationConfig.class);
if (beansNames.length < 2) { // If the number of ApplicationConfig beans is less than two, return immediately.
return;
}
// 在有多个配置情况下,对满足条件的beansNames进行筛选移除
Stream.of(beansNames)
.filter(this::isConfiguredApplicationConfigBeanName)
.forEach(registry::removeBeanDefinition);
//在spring的所有beanDefinitionNames中遍历,找出beanclass类型是org.apache.dubbo.config.ApplicationConfigbeansName(第二次检测)
beansNames = beanNamesForTypeIncludingAncestors(beanFactory, ApplicationConfig.class);
//两次检测后,ApplicationConfig让然有多个的话,则项目启动报错
if (beansNames.length > 1) {
throw new IllegalStateException(String.format("There are more than one instances of %s, whose bean definitions : %s",
ApplicationConfig.class.getSimpleName(),
Stream.of(beansNames)
.map(registry::getBeanDefinition)
.collect(Collectors.toList()))
);
}
}
上述代码中的几个关键点注释已加以说明,其中的对beansNames进行筛选移除方法isConfiguredApplicationConfigBeanName代码如下:
private boolean isConfiguredApplicationConfigBeanName(String beanName) {
boolean removed = BeanFactoryUtils.isGeneratedBeanName(beanName)
// Dubbo ApplicationConfig id as bean name
|| Objects.equals(beanName, environment.getProperty("dubbo.application.id"));
if (removed) {
if (logger.isWarnEnabled()) {
logger.warn("The {} bean [ name : {} ] has been removed!", ApplicationConfig.class.getSimpleName(), beanName);
}
}
return removed;
}
判断beanName是否是自动生成的,自动生成的类似org.apache.dubbo.config.ApplicationConfig#0,#后面数字根据配置数量自动累加。或者与配置文件中配置的dubbo.application.id的值相等,过滤出来后删除。(备注:xml方式的<dubbo:application 以配置的id,name,org.apache.dubbo.config.ApplicationConfig顺序选择作为beanName)
DubboConfigBeanDefinitionConflictProcessor的加载机制
关于DubboConfigBeanDefinitionConflictProcessor的加载机制,不是本篇的重点,这里大概说下:
- 在boot项目启动时候会调用SpringApplication的构造方法,其中有段代码:
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
- 是通过SpringFactoriesLoader记载META-INF/spring.factories里面的配置,在dubbo-spring-boot-autoconfigure-compatible-2.7.3.jar包里面有一行
org.springframework.context.ApplicationContextInitializer=org.apache.dubbo.spring.boot.context.DubboApplicationContextInitializer
- 将DubboApplicationContextInitializer加载到initializers里面,然后在下面代码中初始化。就完成了将DubboConfigBeanDefinitionConflictProcessor放入到bean工厂后置处理器beanFactoryPostProcessors中。
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
public class DubboApplicationContextInitializer implements ApplicationContextInitializer, Ordered {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
overrideBeanDefinitions(applicationContext);
}
private void overrideBeanDefinitions(ConfigurableApplicationContext applicationContext) {
applicationContext.addBeanFactoryPostProcessor(new OverrideBeanDefinitionRegistryPostProcessor());
applicationContext.addBeanFactoryPostProcessor(new DubboConfigBeanDefinitionConflictProcessor());
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}
冲突检测导致dubbo消费者的全局配置不生效
经过先导片的介绍,下来介绍下冲突检测导致dubbo消费者的全局配置不生效的问题。上面说了这个代码:
//在spring的所有beanDefinitionNames中遍历,找出beanclass类型是org.apache.dubbo.config.ApplicationConfigbeansName
String[] beansNames = beanNamesForTypeIncludingAncestors(beanFactory, ApplicationConfig.class);
就是在beanNamesForTypeIncludingAncestors里面,调用了String[] result = lbf.getBeanNamesForType(type); 这是关键点。
看下getBeanNamesForType(type):
@Override
public String[] getBeanNamesForType(@Nullable Class<?> type) {
return getBeanNamesForType(type, true, true);
}
这里面直接将后面的两个参数设置为true!!!!,什么意思呢,不着急,来往下看。
@Override
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
//这里进入到这个条件中,一切的一切,都在doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit)里面;
return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
}
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
String[] resolvedBeanNames = cache.get(type);
if (resolvedBeanNames != null) {
return resolvedBeanNames;
}
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
cache.put(type, resolvedBeanNames);
}
return resolvedBeanNames;
}
doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) 里面的代码太长,此处截取一部分说明下:
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<>();// Check all bean definitions.
for (String beanName : this.beanDefinitionNames) {
// Only consider bean as eligible if the bean name
// is not defined as alias for some other bean.
if (!isAlias(beanName)) {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
// In case of FactoryBean, match object created by FactoryBean.
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
boolean matchFound =
(allowEagerInit || !isFactoryBean ||
(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
(includeNonSingletons ||
(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
isTypeMatch(beanName, type);
if (!matchFound && isFactoryBean) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
}
if (matchFound) {
result.add(beanName);
}
}
}
}
。。。。。。。。省率。。。。。
return StringUtils.toStringArray(result);
}
可以看到这里面是遍历所有的beanDefinitionNames,去匹配对应类型。
boolean matchFound =(allowEagerInit || !isFactoryBean || (dbd != null && !mbd.isLazyInit()) ||containsSingleton(beanName)) // 第一层逻辑判断
&&(includeNonSingletons ||(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) // 第二层逻辑判断
&& isTypeMatch(beanName, type);// 第三层逻辑判断
if (!matchFound && isFactoryBean) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
}
在上面的第一层判断中 allowEagerInit 是上层调用传进来的,是true。到第二层,第二层includeNonSingletons 也是上层调用传进来的,是true。然后进入第三层的isTypeMatch(beanName, type);
isTypeMatch(beanName, type)里面的代码就不粘了,看下这段重要的代码:
// 注释1
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});
//注释2
Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
if (beanType == null) {
return false;
}
//注释3
if (FactoryBean.class.isAssignableFrom(beanType)) {
//注释4
if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
//注释5
beanType = getTypeForFactoryBean(beanName, mbd);
if (beanType == null) {
return false;
}
}
}
注释2中, 是根据beanName推测beandefinition里面的beanClass。
注释3中, 这个时候会判断beanType,对于当beanType 是org.apache.dubbo.config.spring.ReferenceBean(dubbo 消费端对于接口的引用配置类)类型的时候,会往下执行。
注释4中,BeanFactoryUtils.isFactoryDereference(name)是判断name是不是&开头,不是的话,会返回gettype,是的话,直接返回org.apache.dubbo.config.spring.ReferenceBean类型。beanInstance 此处一定是null。因为还没有实例化。
注释5中,getTypeForFactoryBean(beanName, mbd),进去看下代码:
@Nullable
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
if (!mbd.isSingleton()) {
return null;
}
try {
FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
return getTypeForFactoryBean(factoryBean);
}
catch (BeanCreationException ex) {
if (ex.contains(BeanCurrentlyInCreationException.class)) {
if (logger.isTraceEnabled()) {
logger.trace("Bean currently in creation on FactoryBean type check: " + ex);
}
}
else if (mbd.isLazyInit()) {
if (logger.isTraceEnabled()) {
logger.trace("Bean creation exception on lazy FactoryBean type check: " + ex);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Bean creation exception on non-lazy FactoryBean type check: " + ex);
}
}
onSuppressedException(ex);
return null;
}
}
哇塞,看到doGetBean,好了,不用看了,doGetBean里面就是实例化的逻辑了。
因为在dubbo解析配置时候加入了DubboConfigBindingBeanPostProcessor后置处理器,是BeanPostProcessor的子类。
而dubbo配置冲突检测DubboConfigBeanDefinitionConflictProcessor 是BeanFactoryPostProcessor的子类。执行时间DubboConfigBeanDefinitionConflictProcessor 早于DubboConfigBindingBeanPostProcessor。到这就知道是因为冲突检测导致dubbo消费者先初始化。这个时候yml里面的配置还没来得及绑定。