最近在做老系统升级(springboot2+dubbo2.7.1+zookeeper+nacos-config),去掉zookeeper的注册中心,替换成nacos2.1版本(阿里云已经不支持1.X版本了)-对应的需要升级springboot和dubbo3。最终升级完成了,其中遇到的诸多问题,能记住多少就总结多少
有在升级过程中,遇到问题的小伙伴,欢迎交流。
这个错误据官方说已经解决了,先看下错误
服务启动时报错:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'BaClient': Injection of @DubboReference dependencies is failed; nested exception is java.lang.IllegalArgumentException: Can not set com.dubbo3.test.api.AaUserServiceDubbo field com.dubbo3.test.consumer.service.BaClient.userService to com.sun.proxy.$Proxy87
at org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.postProcessPropertyValues(ReferenceAnnotationBeanPostProcessor.java:326)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1418)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:337)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325)
at com.xsyx.router.client.ClientApplication.main(ClientApplication.java:16)
Caused by: java.lang.IllegalArgumentException: com.dubbo3.test.api.AaUserServiceDubbo field com.dubbo3.test.consumer.service.BaClient.userService to com.sun.proxy.$Proxy87
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:764)
at org.apache.dubbo.config.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor$AnnotatedInjectElement.inject(AbstractAnnotationBeanPostProcessor.java:470)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.postProcessPropertyValues(ReferenceAnnotationBeanPostProcessor.java:322)
... 17 common frames omitted
修改BaClient AaUserServiceDubbo userService -> AaUserServiceDubbo aaUserServiceDubbo 后启动成功
因为我还有个服务UserServiceDubbo userServiceDubbo
我理解原因是spring在生成bean BaClient的过程中,当赋值属性AaUserServiceDubbo userServiceDubbo时,是根据名称userServiceDubbo查找对应的bean,而不是根据类型AaUserServiceDubbo查找bean,这个是spring的流程,应该不算dubbo的bug
我现在的dubbo版本是3.1.0 ,我看已经在注册时使用rename方式了。这也是要说的第二个问题。
userServiceDubbo#2
看看问题
Already exists another bean definition with the same bean name [userServiceDubbo], rename dubbo reference bean to [userServiceDubbo#2].
It is recommended to modify the name of one of the beans to avoid injection problems. prev: userServiceDubbo[ReferenceBean:com.dubbo3.test.api.UserServiceDubbo()],
new: userServiceDubbo#2[ReferenceBean:com.dubbo3.test.api.AaUserServiceDubbo()].
Please check private com.dubbo3.test.api.AaUserServiceDubbo com.dubbo3.test.consumer.service.CaClient.userServiceDubbo
这个时什么意思呢,简单说,就是,相同的bean 字段名单,但是接口不同名,发现类型不同,注册时,重新命名userServiceDubbo#2
修改下AaUserServiceDubbo aaUserServiceDubbo 就可以了。
可以看下源码:ReferenceAnnotationBeanPostProcessor
public String registerReferenceBean(String propertyName, Class<?> injectedType, Map<String, Object> attributes, Member member) throws BeansException {
boolean renameable = true;
// referenceBeanName
String referenceBeanName = getAttribute(attributes, ReferenceAttributes.ID);
if (hasText(referenceBeanName)) {
renameable = false;
} else {
referenceBeanName = propertyName;
}
String checkLocation = "Please check " + member.toString();
// convert annotation props
ReferenceBeanSupport.convertReferenceProps(attributes, injectedType);
// get interface
String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE);
if (StringUtils.isBlank(interfaceName)) {
throw new BeanCreationException("Need to specify the 'interfaceName' or 'interfaceClass' attribute of '@DubboReference' if enable generic. " + checkLocation);
}
// check reference key
String referenceKey = ReferenceBeanSupport.generateReferenceKey(attributes, applicationContext);
// find reference bean name by reference key
List<String> registeredReferenceBeanNames = referenceBeanManager.getBeanNamesByKey(referenceKey);
if (registeredReferenceBeanNames.size() > 0) {
// found same name and reference key
if (registeredReferenceBeanNames.contains(referenceBeanName)) {
return referenceBeanName;
}
}
//check bean definition
if (beanDefinitionRegistry.containsBeanDefinition(referenceBeanName)) {
BeanDefinition prevBeanDefinition = beanDefinitionRegistry.getBeanDefinition(referenceBeanName);
String prevBeanType = prevBeanDefinition.getBeanClassName();
String prevBeanDesc = referenceBeanName + "[" + prevBeanType + "]";
String newBeanDesc = referenceBeanName + "[" + referenceKey + "]";
if (isReferenceBean(prevBeanDefinition)) {
//check reference key
String prevReferenceKey = ReferenceBeanSupport.generateReferenceKey(prevBeanDefinition, applicationContext);
if (StringUtils.isEquals(prevReferenceKey, referenceKey)) {
//found matched dubbo reference bean, ignore register
return referenceBeanName;
}
//get interfaceName from attribute
Assert.notNull(prevBeanDefinition, "The interface class of ReferenceBean is not initialized");
prevBeanDesc = referenceBeanName + "[" + prevReferenceKey + "]";
}
// bean name from attribute 'id' or java-config bean, cannot be renamed
if (!renameable) {
throw new BeanCreationException("Already exists another bean definition with the same bean name [" + referenceBeanName + "], " +
"but cannot rename the reference bean name (specify the id attribute or java-config bean), " +
"please modify the name of one of the beans: " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
}
// the prev bean type is different, rename the new reference bean
int index = 2;
String newReferenceBeanName = null;
while (newReferenceBeanName == null || beanDefinitionRegistry.containsBeanDefinition(newReferenceBeanName)
|| beanDefinitionRegistry.isAlias(newReferenceBeanName)) {
newReferenceBeanName = referenceBeanName + "#" + index;
index++;
// double check found same name and reference key
if (registeredReferenceBeanNames.contains(newReferenceBeanName)) {
return newReferenceBeanName;
}
}
newBeanDesc = newReferenceBeanName + "[" + referenceKey + "]";
logger.warn("Already exists another bean definition with the same bean name [" + referenceBeanName + "], " +
"rename dubbo reference bean to [" + newReferenceBeanName + "]. " +
"It is recommended to modify the name of one of the beans to avoid injection problems. " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
referenceBeanName = newReferenceBeanName;
}
attributes.put(ReferenceAttributes.ID, referenceBeanName);
// If registered matched reference before, just register alias
if (registeredReferenceBeanNames.size() > 0) {
beanDefinitionRegistry.registerAlias(registeredReferenceBeanNames.get(0), referenceBeanName);
referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
return referenceBeanName;
}
Class interfaceClass = injectedType;
// TODO Only register one reference bean for same (group, interface, version)
// Register the reference bean definition to the beanFactory
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(ReferenceBean.class.getName());
beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName);
// set attribute instead of property values
beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
// create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
// see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
targetDefinition.setBeanClass(interfaceClass);
beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, referenceBeanName + "_decorated"));
// signal object type since Spring 5.2
beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
logger.info("Register dubbo reference bean: " + referenceBeanName + " = " + referenceKey + " at " + member);
return referenceBeanName;
}