spring-boot中dubbo的配置冲突检测导致dubbo消费者的全局配置不生效

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的加载机制,不是本篇的重点,这里大概说下:

  1. 在boot项目启动时候会调用SpringApplication的构造方法,其中有段代码:
setInitializers((Collection) getSpringFactoriesInstances(	ApplicationContextInitializer.class));
  1. 是通过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
  1. 将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里面的配置还没来得及绑定。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值