Dubbo配置注册中心设置application的name使用驼峰命名法存在的隐藏项目启动异常问题
原创/朱季谦
首先,先提一个建议,在SpringBoot+Dubbo项目中,Dubbo配置注册中心设置的application命名name的值,最好使用xxx-xxx-xxx这样格式的,避免随便使用驼峰命名。因为使用驼峰命名法,在Spring的IOC容器当中,很可能会出现一些导致项目启动失败的坑,例如,会出现这样的异常报错:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userController': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userService' is expected to be of type 'com.xxx.xxx.xxx.service.UserService' but was actually of type 'org.apache.dubbo.config.ApplicationConfig'
在说明该问题之前,首先,需要提一下org.apache.dubbo.config.ApplicationConfig这个类,这是一个Dubbo的应用配置类,它用在哪里呢?
在SpringBoot 2.x+Dubbo项目当中,主流都是使用yaml文件设置项目环境依赖参数,不同的组件,其配置类的实例化各有差异。本文主要简单分析下Dubbo的相关配置类是如何通过yaml文件参数来根据环境不同进行做相应的实例化。
Dubbo初始化配置类主要有以下——
| 序号 | 类名 | 用途 | 说明 |
|---|---|---|---|
| 1 | ApplicationConfig | 当前应用配置 | 提供者或者消费者配置当前应用信息,一般以属性name区分各应用 |
| 2 | MonitorConfig | 监控中心配置 | 配置连接监控中心monitor参数 |
| 3 | RegistryConfig | 连接注册中心配置 | 配置Dubbo用到的注册中心 |
| 4 | ProtocolConfig | 服务提供者协议配置 | 配置提供方的远程通信协议 |
| ...... | ...... | ...... | ...... |
这些配置类的实现原理基本大同小异,本文主要以ApplicationConfig配置类做讲解分析。
在yaml配置文件里,Dubbo的配置例子如下——
dubbo:
application:
name: userService
registry:
address: zookeeper://127.0.0.1:2181
timeout: 10000
protocol:
name: dubbo
port: 20880
这个配置可以拆开如下图这样看,便能一眼看懂它们分别属于哪个配置类——

当在yaml文件这样配置后,当项目启动时,会自动获取这些参数,然后初始化到对应的配置类当中,例如,application中的name值就会设置到ApplicationConfig类对象里——

在SpringBoot中,这个ApplicationConfig对象会在普通bean初始化之前,就已经装载到IOC容器当中,以name的值做该bean名,同时,会以name:className的方式存储在Spring的bean别名缓存aliasMap当中,这就出现一个问题,假如该项目当中存在同名bean注解的话,会出现什么样情况呢?
例如,当SpringBoot的Dubbo配置如前边一样,以字符串“userService”做ApplicationConfig的name值,同时,controller层有以下代码——
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
......
}
我们可以在IOC容器过程的AbstractBeanFactory类中的doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)方法截图处打一个针对userService的断点——

截图里的逻辑,其实是在对注解有@RestController的UserController类做IOC过程中,会对其通过 @Resource设置的属性userService做依赖注入过程,首先,会去bean别名aliasMap缓存当中看是否能查询到,我们进入到transformedBeanName(name)方法底层——
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
此时,this.aliasMap缓存里已经有值了,主要都是Dubbo相关的,这说明Dubbo会在普通自定义Bean前就做了IOC注入,我们可以看到,前边提到的ApplicationConfig对象class类名,已经缓存在aliasMap当中,其key值,正好yaml配置文件里设置的name值。当以“userService”字符串取aliasMap获取,是可以拿到值的——

但是,这里注意一点,此刻debug这一步doGetBean,理应依赖注入的是UserService类而不是ApplicationConfig类——

然而实际情况是,此时,通过方法Object sharedInstance = getSingleton(beanName)从IOC三级缓存之一的单例池里获取到的则是ApplicationConfig已经初始化成单例bean的对象——

这将会出现什么情况呢?
在 doGetBean方法最后,会做一步这样操作,将需要初始化的bean类型requiredType与通过“userService”从单例池里获取到的实际bean类型做比较——
// Check if required type matches the type of the actual bean instance.
//检查所需类型是否与实际bean实例的类型匹配
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
结果可想而知,一个是UserService类,一个是ApplicationConfig类,两者肯定不匹配,那么就会执行抛出异常throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
public BeanNotOfRequiredTypeException(String beanName, Class<?> requiredType, Class<?> actualType) {
super("Bean named '" + beanName + "' is expected to be of type '" + ClassUtils.getQualifiedName(requiredType) +
"' but was actually of type '" + ClassUtils.getQualifiedName(actualType) + "'");
this.beanName = beanName;
this.requiredType = requiredType;
this.actualType = actualType;
}
debug到这一步,其错误提示,刚好就是——
Bean named 'userService' is expected to be of type 'com.xxx.xxx.xxx.service.UserService' but was actually of type 'org.apache.dubbo.config.ApplicationConfig
因此,就说明一个问题,当Dubbo应用配置application的name使用驼峰命名,例如,本文中的userService,刚好又有某个地方用到类似这样注解的属性依赖注入 private UserService userService,那么,项目在启动过程中,就会出现类似本文中提到的项目启动异常。
可见,在application的name值使用xxx-xxx-xx这样方式命名会更好些。
Dubbo配置注册中心设置application的name使用驼峰命名法存在的隐藏项目启动异常问题的更多相关文章
- Javaconfig形式配置Dubbo多注册中心
多注册中心,一般用不到,但是某些情况下的确能解决不少问题,可以将某些dubbo服务注册到2套dubbo系统中,实现服务在2套系统间的共用. 网上的配置说明很多,但包括dubbo官方说明文档都是以xml ...
- Dubbo多注册中心和Zookeeper服务的迁移
一.Dubbo多注册中心 1. 应用场景 例如阿里有些服务来不及在青岛部署,只在杭州部署,而青岛的其它应用需要引用此服务,就可以将服务同时注册到两个注册中心. consumer.xml <?xm ...
- 如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他
注册中心作用 开篇首先想思考一个问题,没有注册中心 Dubbo 还能玩下去吗? 当然可以,只要知道服务提供者地址相关信息,消费者配置之后就可以调用.如果只有几个服务,这么玩当然没问题.但是生产服务动辄 ...
- Dubbo Multicast 注册中心即相关代码实现
Dubbo 的 Multicast注册中心有下面特点: 不需要启动任何中心节点,只要广播地址一样,就可以互相发现 组播受网络结构限制,只适合小规模应用或开发阶段使用. 组播地址段: 224.0.0.0 ...
- Dubbo及注册中心原理
本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天是猿灯塔“365天原创计划”第4天. 今天呢!灯塔君跟大家讲: 一.Dubbo意义 网站应用的架构变化经历了一个从所有服务分布在一台服务器上(A ...
- Dubbo多注册中心
一.创建提供者08-provider-registers (1) 创建工程 直接复制05-provider-group工程,并命名为08-provider-registers (2) 修改配置文件 二 ...
- dubbo 多注册中心
这个我调试了下,多个注册中心在创建代理的时候,每个注册中心对应一个invoker,持有一个RegistryDirectory对应一个zkClinet,并且维护这样一个map: 那些不正确zk在创建代理 ...
- SpringCloud:Eureka注册中心设置显示IP路径
未设置下的Eureka平台 可以看到Status显示的是 计算机名称! 解决方法: 在每一个需要注册的服务配置内加上如下几行配置 instance: prefer-ip-address: true # ...
- spring cloud配置注册中心显示服务的ip地址和端口
1.在springcloud中服务的 Instance ID 默认值是: ${spring.cloud.client.hostname}:${spring.application.name}:${sp ...
随机推荐
- [atAGC106E]Medals
暴力二分答案+网络流,点数为$o(nk)$,无法通过 考虑Hall定理,即有完美匹配当且仅当$\forall S\subseteq V_{left}$,令$S'=\{x|\exists y\in V_ ...
- [loj2478]林克卡特树
原题等价于选择恰好$k+1$条不相交(无公共点)的路径使得边权和最大 证明:对于原题中的最优解,一定包含了k条0边权的边(否则可以将未使用的边删掉,然后将这条路径的末尾与不在同一个连通块内的点连边), ...
- 深度学习,机器学习神器,白嫖免费GPU
深度学习,机器学习神器,白嫖免费GPU! 最近在学习计算机视觉,自己的小本本没有那么高的算力,层级尝试过Google的Colab,以及移动云的GPU算力,都不算理想.如果数据集比较小,可以试试Cola ...
- BehaviorTree.CPP行为树BT的入门(二)
节点与树 用户必须创建自己的ActionNodes和ConditionNodes(LeafNodes):该库可帮助您轻松地将它们组成树. 将LeafNodes视为组成复杂系统所需的构建块. 根据定义, ...
- 洛谷 P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I(分块+卡常)
洛谷题面传送门 zszz,lxl 出的 DS 都是卡常题( 首先由于此题强制在线,因此考虑分块,我们那么待查询区间 \([l,r]\) 可以很自然地被分为三个部分: 左散块 中间的整块 右散块 那么这 ...
- 洛谷 P5540 - [BalkanOI2011] timeismoney | 最小乘积生成树(最小生成树)
洛谷题面传送门 大概是一个比较 trivial 的小 trick?学过了就不要忘了哦( 莫名奇妙地想到了 yyq 的"hot tea 不常有,做过了就不能再错过了" 首先看到这种二 ...
- 查看nginx(Web网页服务器)状态是否正常
Linux每个应用运行都会产生一个进程,那么我们就可以通过查看Nginx进程是否存在来判断它是否启动. 1.有时想知道nigix是否在正常运行,需要用linux命令查看nginx运行情况.执行命令:p ...
- Spark3学习【基于Java】3. Spark-Sql常用API
学习一门开源技术一般有两种入门方法,一种是去看官网文档,比如Getting Started - Spark 3.2.0 Documentation (apache.org),另一种是去看官网的例子,也 ...
- 半天做完的数据报表,YonBuilder只要十几分钟,0代码开发
进入数字化时代,拍脑袋的决策方式显然不靠谱,一切要靠数据说话.与信息化时代相比,数字化时代的企业对数据的应用更广泛.更深入.为了应对激烈的市场竞争,企业经营决策者们对数据的依赖度越来越高,企业各个业务 ...
- applogs流量数据项目学习
一. 项目介绍 项目的功能主要是面向App开发商提供App使用情况的统计服务 主要是基于用户启动app的统计分析,app只要启动就会上报一条日志记录 (启动日志),当然也会有其他的日志比如说页面访问日 ...