前言

  今天下午,当我经过一个小时的奋”键“疾”码“,准备好好的审查一下(摸鱼)自己写的代码,经过一段时间审查(摸的差不多了,该下班了),得出一个结论我写的代码很优雅、精简。所以大手一挥提交代码,并在 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将参数正常的传递过来了。那么问题应该就出在EmployeeConvertAddEmployeeDTO转换为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属性指向了AddEmployeeFormemployees的内存地址。所以将在进行调用时,Dubbo因为反序列化时找不到对应的类,就会将其转换为Map

小结一下

  上面的问题,主要是由于 BeanUtils 浅拷贝造成。并且引发连锁反应,造成Dubbo反序列化异常以及EmployeeConvert的转换异常,最后抛出了java.util.HashMap cannot be cast to com.aixiao.inv.common.dto.tax.AddEmployeeDTO$Employee 错误信息。

解决方法

  既然知道了问题出现的原因,那么解决起来就很简单了。对于单一的属性,那么不涉及到深拷贝的问题,适合用 BeanUtils 继续进行拷贝。但是涉及到集合我们可以这样处理:

  1. 简单粗暴使用 foreach 进行拷贝。

  2. 使用 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()));
  1. 封装一个转换类进行转换。
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);}

总结

  1. 使用 BeanUtils.copyProperties()进行拷贝需要注意
  2. dubbo 在进行反序列化的时候,如果找不到对应类会将其转化为 map。

参考

  • BeanUtils.copyProperties 的使用(深拷贝,浅拷贝)

结尾

  我是不一样的科技宅,每天进步一点点,体验不一样的生活。我们下期见!

  如果觉得对你有帮助,可以多多评论,多多点赞哦,也可以到我的主页看看,说不定有你喜欢的文章,也可以随手点个关注哦,谢谢。


map insert异常失败_处理dubbo反序列化失败的坑相关推荐

  1. java反序列化失败怎么处理_处理dubbo反序列化失败的坑

    kubernetes权威指南从docker到实践 79.8元 包邮 (需用券) 去购买 > 前言 今天下午,当我经过一个小时的奋"键"疾"码",准备好好的 ...

  2. java反序列化流建立失败_关于java:处理dubbo反序列化失败的坑

    前言 今天下午,当我通过一个小时的奋"键"疾"码",筹备好好的审查一下(摸鱼)本人写的代码,通过一段时间审查(摸的差不多了,该上班了),得出一个论断我写的代码很 ...

  3. Beanutils造成dubbo反序列化失败?

    前言   今天下午,当我经过一个小时的奋"键"疾"码",准备好好的审查一下(摸鱼)自己写的代码,经过一段时间审查(摸的差不多了,该下班了),得出一个结论我写的代 ...

  4. cassandra本地连接失败_本地网络发现失败的解决方法连接到OS X中的服务器的问题 | MOS86...

    本地联网通常在Mac上完美无缺,这就是为什么某些用户可能会遇到OS X优胜美地的一些最恶化的问题与网络连接有关.其中一些可能涉及更广泛的连接和Wi-Fi功能问题,以及其他可能影响一般LAN网络功能的能 ...

  5. 信息记录拉取失败_天猫入驻为什么失败?猫店侠做详细解读

    天猫入驻为什么失败?这是很多商家都想要知道的一件事情,猫店侠想说其实这也很正常,只要不是一味盲目的入驻,就还有机会. 首先失败商家要看看失败的反馈内容,看看是哪方面不达标,再着重进行补充,但实际上猫店 ...

  6. 创建试图 失败_导致微服务失败的 11 个原因

    作者 | Shekhar Gulati@Medium 译者 | Sambodhi 策划 | Tina@infoq 微服务"很香",它有许多优势,比如更快的开发.更好的可扩展性.更小 ...

  7. 应用连接mysql数据库失败_连接MySQL数据库失败频繁的原因

    连接mySQL数据库失败频繁,主要是什么原因造成的? 一年前,我开发了一个网站,租用的是linux下PHP+mySQL的虚拟空间,之前一直工作正常. 近半年来,却频繁出现连接数据库失败错误(一周1~2 ...

  8. 苹果cms安装mysql检测失败_苹果cms安装失败的几种解决方法

    咱们今天继续说下苹果cms关于安装相关的几个常见问题,苹果cms安装失败的几种解决方法.苹果cms在安装时总是会遇到这样或是那样的问题,今天做个简单的总结吧.一起来互相学习和交流. 1,苹果cms一直 ...

  9. 苹果cms安装mysql检测失败_苹果cms安装失败怎么回事?苹果cms安装失败的几种解决方法...

    这篇文章主要为大家详细介绍了苹果cms安装失败怎么回事?苹果cms安装失败的几种解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借鉴. 咱们今天继续说下苹果cm ...

最新文章

  1. linux文件时间属性的详解
  2. Track与nqa联动 VS 静态路由优先级相同
  3. Python爬虫初学(三)—— 模拟登录知乎
  4. POJ 1003 Hangover
  5. win7双系统安装openSUSE13.2解决【引导加载器安装期间出错】问题
  6. linux php扩展 mysqli,[linux]PHP添加mysqli扩展 | 学步园
  7. DockerFile最佳实践:
  8. Pytorch——分类问题
  9. springboot 添加 lombok 报错更新 版本号
  10. arcobjects java开发_ArcGIS Engine SDK for Java 最小示例学习
  11. Lotus notes 通讯录的导入导出
  12. 利用rpm命令安装软件
  13. 《FaceBoxes: A CPU Real-time Face Detector with High Accuracy》论文笔记
  14. 如何定制App Store榜单优化策略?
  15. 计算机组成原理页表长度,清华计算机组成原理习题课课件习题课1-7.ppt
  16. ADC0809转换器
  17. Mp4文件播放原理分析
  18. 与计算机专业相关的英语科普短文,求科普类的简单的英语小短文
  19. ubuntu20.4安装ROS2 Noetic Ninjem
  20. “大灰狼”远控木马分析及幕后真凶调查

热门文章

  1. teamviewer设备数量上限怎么解决_支小蜜刷脸支付设备解决方案,支小蜜刷脸支付设备怎么使用...
  2. SSD300网络结构(pytorch)+多尺度训练与测试
  3. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 18丨即时食物配送 I【难度简单】​
  4. Java加密与解密的艺术~MD算法实现
  5. Python编程从入门到实践~if语句
  6. python分类下取得所有子类_如何找到给定名称的类的所有子类?
  7. [错误记录] --- clickhouse报错Decimal value is too small
  8. html5文章页面,文章页面的设计《 HTML5 与 Bootstrap 应用实例 》
  9. php重复点击按钮无效,完美解决UIButton按钮重复点击、多次响应的问题
  10. java 3位小数_数字有效小数第三位四舍五入