map insert异常失败_处理dubbo反序列化失败的坑
前言
今天下午,当我经过一个小时的奋”键“疾”码“,准备好好的审查一下(摸鱼)自己写的代码,经过一段时间审查(摸的差不多了,该下班了),得出一个结论我写的代码很优雅、精简。所以大手一挥提交代码,并在 API 管理系统上将 xxx 接口点了个完成。准备收拾东西走人了准点下班。然而事与愿违,没过多久前端大哥就@我了,说 xxx 接口有问题,麻烦处理一下。内心第一反应(你丫的参数传错了吧)卑微的我只能默默的回个,好的、麻烦把参数给我一下,我这边检查一下[微笑脸]。
场景还原
经过测试,发现确实是我的问题。还好没甩锅,要不然就要被打脸了。错误信息如下:
{ "code": "010000", "message":"java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee", "data": null}
看到这个错误有点懵,HashMap
无法转换为AddEmployeeDTO$Employee
。内心在想,没道理啊。请求参数我都是拷贝过来的,压根就没用Map
进行参数传递。毕竟我都是个老手了,咋可能犯这样愚蠢的错误。俗话说遇到问题不要慌,让我们掏出手机先发个朋友圈,不对好像有点跑题了,我们先看一下调用链的数据传递。
首先 web 将AddEmployeeForm
数据传递到服务端,然后使用fromToDTO()
方法,进行将数据转换为 Dubbo 请求需要的AddEmployeeDTO
。Dubbo 服务放接收AddEmployeeDTO
后,使用 EmployeeConvert
将数据转换为AddEmployeeXmlReq
再执行相关逻辑。
AddEmployeeForm 类
@Datapublic class AddEmployeeForm implements Serializable {
/** * 职员信息列表 */ private List employees;@Datapublic static class Employee implements Serializable {/** * 姓名 */private String name;/** * 工作 */private String job; }}
FormToDTO()方法
public T formToDTO(F form, T dto) {// 进行数据拷贝 BeanUtils.copyProperties(form, dto);// 返回数据return dto;}
AddEmployeeDTO 类
@Datapublic class AddEmployeeDTO implements Serializable {
/** * 职员信息列表 */ private List employees;@Datapublic static class Employee implements Serializable {/** * 姓名 */private String name;/** * 工作 */private String job; }}
EmployeeConvert 转换类
❝
EmployeeConvert 转换类,使用了mapstruct进行实现,没使用过的小伙伴可以简单的了解下。
❞
@Mapperpublic interface EmployeeConvert {
EmployeeConvert INSTANCE = Mappers.getMapper(EmployeeConvert.class);
AddEmployeeXmlReq dtoToXmlReq(AddEmployeeDTO dto);
}
AddEmployeeXmlReq 类
@Datapublic class AddEmployeeXmlReq implements Serializable {
/** * 职员信息列表 */ private List employees;@Datapublic static class Employee implements Serializable {/** * 姓名 */private String name;/** * 工作 */private String job; }}
EmployeeController
@RestController@AllArgsConstructorpublic class EmployeeController {
private final EmployeeRpcProvider provider;
@PostMapping("/employee/add") public ResultVO employeeAdd(@RequestBody AddEmployeeForm form) { provider.add(formToDTO(form,new AddEmployeeDTO())); return ResultUtil.success(); }}
EmployeeRpcServiceImpl
@Slf4j@Servicepublic class EmployeeRpcServiceImpl implements EmployeeService {
@Override public ResultDTO add(AddEmployeeDTO dto) { log.info("dubbo-provider-AddEmployeeDTO:{}", JSON.toJSONString(dto)); AddEmployeeXmlReq addEmployeeXmlReq = EmployeeConvert.INSTANCE.dtoToXmlReq(dto); return ResultUtil.success(); }}
分析原因
判断异常抛出点
我们需要先确定异常是在consumer
抛出的还是provider
抛出的。判断过程很简单,我们可以进行本地debug
,看看是执行到哪里失败了就知道了。如果不方便本地调试,我们可以在关键点上打上相应的日志。比如说consumer
调用前后,provider
处理前后。如果请求正常 日志打印的顺序应该是:
这样通过观察日志就可以判定异常是在哪里抛出的了。
❝
实际并没有这样麻烦,因为在 consumer 做了 rpc 异常拦截,所以我当时看了下 consumer 的日志就知道是 provider 抛出来的。
❞
找到出错的代码
既然找到了出问题是出在provider
,那看是什么原因导致的,从前面的调用链可以知道,provider
接收到AddEmployeeDTO
会使用EmployeeConvert
将其转换为AddEmployeeXmlReq
,所以我们可以打印出AddEmployeeDTO
看看consumer
的传参是否正常。
通过日志我们可以发现consumer
将参数正常的传递过来了。那么问题应该就出在EmployeeConvert
将AddEmployeeDTO
转换为AddEmployeeXmlReq
这里了。由于EmployeeConvert
是使用mapstruct进行实现,我们可以看看自动生成的转换类实现逻辑是咋样的。
通过观察源代码可以发现,在进行转换的时候需要传入一个List
而这个Employee
正是AddEmployeeDTO.Employee
。这个时候可能会困扰了,我明明就是传入AddEmployeeDTO
,而且类里面压根就没有Map
,为啥会抛出java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee
这个异常呢?
让我们Debug
一下看看发生了啥。
这个时候你会发现接收到的AddEmployeeDTO.employees
内存储的并不是一个AddEmployeeDTO$Employee
对象,而是一个HashMap
。那看来真相大白了,原来是 dubbo 反序列化的时候将AddEmployeeDTO$Employee
转换为HashMap
了。从而导致了java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee
异常的抛出。
你以为结束了?
为啥Dubbo
反序列化时会将AddEmployeeDTO$Employee
变成Map
呢?我们回过头看看之前打印参数的日志,有一个警告日志提示了java.lang.ClassNotFoundException:com.aixiao.inv.api.model.form.AddEmployeeForm$Employee
,找不到AddEmployeeForm$Employee
这个就有点奇怪了,为啥不是AddEmployeeDTO$Employee
?
在进行dubbo
调用前AddEmployeeForm
会使用fromToDTO()
方法将其转化为AddEmployeeDTO
。那么问题会不会出现在这里呢?我们继续Debug
看看。
呕吼,这下石锤了。原来是在formToDTO
的时候出问题了。传递过去AddEmployeeDTO
内部的Employee
竟然变成了AddEmployeeForm$Employee
。这也是为什么provider
那边会抛出java.lang.ClassNotFoundException:com.aixiao.inv.api.model.form.AddEmployeeForm$Employee
的原因了。审查一下formToDTO
的代码看看为啥会发生这样的情况:
public T formToDTO(F form, T dto) {// 进行数据拷贝 BeanUtils.copyProperties(form, dto);// 返回数据return dto;}
fromToDTO
内的代码非常精简,就一个BeanUtils.copyProperties()
的方法,那毫无疑问它就是罪魁祸首了。通过在 baidu 的海洋里遨游,我找到了原因。原来是BeanUtils
是浅拷贝造成的。浅拷贝只是调用子对象的 set 方法,并没有将所有属性拷贝。(也就是说,引用的一个内存地址),所以在转换的时候,将AddEmployeeDTO
内的employees
属性指向了AddEmployeeForm
的employees
的内存地址。所以将在进行调用时,Dubbo
因为反序列化时找不到对应的类,就会将其转换为Map
。
小结一下
上面的问题,主要是由于 BeanUtils 浅拷贝造成。并且引发连锁反应,造成Dubbo
反序列化异常以及EmployeeConvert
的转换异常,最后抛出了java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee
错误信息。
解决方法
既然知道了问题出现的原因,那么解决起来就很简单了。对于单一的属性,那么不涉及到深拷贝的问题,适合用 BeanUtils 继续进行拷贝。但是涉及到集合我们可以这样处理:
简单粗暴使用 foreach 进行拷贝。
使用 labmda 实现进行转换。
AddEmployeeDTO dto = new AddEmployeeDTO();dto.setEmployees(form.getEmployees().stream().map(tmp -> { AddEmployeeDTO.Employee employee = new AddEmployeeDTO.Employee(); BeanUtils.copyProperties(tmp,employee); return employee;}).collect(Collectors.toList()));
- 封装一个转换类进行转换。
AddEmployeeDTO dto = new AddEmployeeDTO();dto.setEmployees(convertList(form.getEmployees(),AddEmployeeDTO.Employee.class));
public List convertList(List source, Class targetClass) {return JSON.parseArray(JSON.toJSONString(source), targetClass);}
总结
- 使用 BeanUtils.copyProperties()进行拷贝需要注意
- dubbo 在进行反序列化的时候,如果找不到对应类会将其转化为 map。
参考
- BeanUtils.copyProperties 的使用(深拷贝,浅拷贝)
结尾
我是不一样的科技宅,每天进步一点点,体验不一样的生活。我们下期见!
如果觉得对你有帮助,可以多多评论,多多点赞哦,也可以到我的主页看看,说不定有你喜欢的文章,也可以随手点个关注哦,谢谢。
map insert异常失败_处理dubbo反序列化失败的坑相关推荐
- java反序列化失败怎么处理_处理dubbo反序列化失败的坑
kubernetes权威指南从docker到实践 79.8元 包邮 (需用券) 去购买 > 前言 今天下午,当我经过一个小时的奋"键"疾"码",准备好好的 ...
- java反序列化流建立失败_关于java:处理dubbo反序列化失败的坑
前言 今天下午,当我通过一个小时的奋"键"疾"码",筹备好好的审查一下(摸鱼)本人写的代码,通过一段时间审查(摸的差不多了,该上班了),得出一个论断我写的代码很 ...
- Beanutils造成dubbo反序列化失败?
前言 今天下午,当我经过一个小时的奋"键"疾"码",准备好好的审查一下(摸鱼)自己写的代码,经过一段时间审查(摸的差不多了,该下班了),得出一个结论我写的代 ...
- cassandra本地连接失败_本地网络发现失败的解决方法连接到OS X中的服务器的问题 | MOS86...
本地联网通常在Mac上完美无缺,这就是为什么某些用户可能会遇到OS X优胜美地的一些最恶化的问题与网络连接有关.其中一些可能涉及更广泛的连接和Wi-Fi功能问题,以及其他可能影响一般LAN网络功能的能 ...
- 信息记录拉取失败_天猫入驻为什么失败?猫店侠做详细解读
天猫入驻为什么失败?这是很多商家都想要知道的一件事情,猫店侠想说其实这也很正常,只要不是一味盲目的入驻,就还有机会. 首先失败商家要看看失败的反馈内容,看看是哪方面不达标,再着重进行补充,但实际上猫店 ...
- 创建试图 失败_导致微服务失败的 11 个原因
作者 | Shekhar Gulati@Medium 译者 | Sambodhi 策划 | Tina@infoq 微服务"很香",它有许多优势,比如更快的开发.更好的可扩展性.更小 ...
- 应用连接mysql数据库失败_连接MySQL数据库失败频繁的原因
连接mySQL数据库失败频繁,主要是什么原因造成的? 一年前,我开发了一个网站,租用的是linux下PHP+mySQL的虚拟空间,之前一直工作正常. 近半年来,却频繁出现连接数据库失败错误(一周1~2 ...
- 苹果cms安装mysql检测失败_苹果cms安装失败的几种解决方法
咱们今天继续说下苹果cms关于安装相关的几个常见问题,苹果cms安装失败的几种解决方法.苹果cms在安装时总是会遇到这样或是那样的问题,今天做个简单的总结吧.一起来互相学习和交流. 1,苹果cms一直 ...
- 苹果cms安装mysql检测失败_苹果cms安装失败怎么回事?苹果cms安装失败的几种解决方法...
这篇文章主要为大家详细介绍了苹果cms安装失败怎么回事?苹果cms安装失败的几种解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借鉴. 咱们今天继续说下苹果cm ...
最新文章
- linux文件时间属性的详解
- Track与nqa联动 VS 静态路由优先级相同
- Python爬虫初学(三)—— 模拟登录知乎
- POJ 1003 Hangover
- win7双系统安装openSUSE13.2解决【引导加载器安装期间出错】问题
- linux php扩展 mysqli,[linux]PHP添加mysqli扩展 | 学步园
- DockerFile最佳实践:
- Pytorch——分类问题
- springboot 添加 lombok 报错更新 版本号
- arcobjects java开发_ArcGIS Engine SDK for Java 最小示例学习
- Lotus notes 通讯录的导入导出
- 利用rpm命令安装软件
- 《FaceBoxes: A CPU Real-time Face Detector with High Accuracy》论文笔记
- 如何定制App Store榜单优化策略?
- 计算机组成原理页表长度,清华计算机组成原理习题课课件习题课1-7.ppt
- ADC0809转换器
- Mp4文件播放原理分析
- 与计算机专业相关的英语科普短文,求科普类的简单的英语小短文
- ubuntu20.4安装ROS2 Noetic Ninjem
- “大灰狼”远控木马分析及幕后真凶调查
热门文章
- teamviewer设备数量上限怎么解决_支小蜜刷脸支付设备解决方案,支小蜜刷脸支付设备怎么使用...
- SSD300网络结构(pytorch)+多尺度训练与测试
- 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 18丨即时食物配送 I【难度简单】​
- Java加密与解密的艺术~MD算法实现
- Python编程从入门到实践~if语句
- python分类下取得所有子类_如何找到给定名称的类的所有子类?
- [错误记录] --- clickhouse报错Decimal value is too small
- html5文章页面,文章页面的设计《 HTML5 与 Bootstrap 应用实例 》
- php重复点击按钮无效,完美解决UIButton按钮重复点击、多次响应的问题
- java 3位小数_数字有效小数第三位四舍五入