java浅拷贝BeanUtils.copyProperties引发的RPC异常

createh54个月前 (02-01)技术教程27

背景

近期参与了一个攻坚项目,前期因为其他流程原因,测试时间已经耽搁了好几天了,本以为已经解决了卡点,后续流程应该顺顺利利的,没想到 人在地铁上,bug从咚咚来~

没有任何修改的服务接口,抛出异常:

java.lang.ClassCastException: java.util.HashMap cannot be cast to cn.xxx.xxx.xxx.xxx.BatchInfo

排查过程

1、作为资深写bug的老司机,第一感觉是传参的报文格式有问题了,可以通过模拟报文排查。于是乎,在群里圈了服务提供方同学B看下,BG快速的用测试工具+本地debug的方式,验证了下报文格式,发现居然都调用成功了。。。

2、同步服务调用同学L,重点关注:1)、调用方的序列化方式;2)、最近代码改动逻辑是否有问题。L同学确认自己逻辑没有问题后,同步B同学和S同学,看内部是否有什么处理逻辑。。。

3、第二天早上一来,快速写了单测,确认服务端收到的报文格式,的确没有问题。于是乎,开始扒代码。。。发现可疑的代码:

BeanUtils.copyProperties(item,cargoInfo)

private List<CargoInfo> convertToCargoInfo(OutboundEventCallbackRequest outboundEventCallbackRequest) {
        return outboundEventCallbackRequest.getCargos().stream().map(item -> {
            CargoInfo cargoInfo = new CargoInfo();
            BeanUtils.copyProperties(item, cargoInfo);
            return cargoInfo;
    }).collect(Collectors.toList());
}

PS:客户端&服务端类关系




因为BeanUtils.copyProperties属于浅拷贝,而浅拷贝只是调用子对象的set方法,并没有将所有属性拷贝(引用的一个内存地址)。所以将在进行调用时,JSF会因为反序列化时找不到对应的类,就会将其转换为Map。

直观图如下:





以上,初步定位原因,解决方式也就清晰了。

解决方案

去掉BeanUtils.copyProperties,进行手动赋值。最终解决了这个问题。


后续反思

1、想起王东岳老师的那句话,越原始的越稳定~

2、如果这种转换比较多,建议使用MapStruct

3、谨慎使用BeanUtils.copyProperties,请看:


相关文章

你知道Java的对象拷贝方式有哪几种吗?

【死记硬背】总共有四种,分别是直接赋值拷贝、浅拷贝、深拷贝和序列化。直接赋值拷贝:这个实际上复制的是对象的引用地址,如:Person p1 = p2,则p1和p2指向的是同一个对象的地址。因此,p1属...

你还在用BeanUtils进行对象属性拷贝?

在做业务的时候,为了隔离变化,我们会将DAO查询出来的DO和对前端提供的DTO隔离开来。大概90%的时候,它们的结构都是类似的;但是我们很不喜欢写很多冗长的b.setF1(a.getF1())这样的代...

java对象深拷贝的三种实现方式,可避免循环引用导致的堆栈溢出

在java编程中,难免要对一个对象进行复制,复制分为深拷贝和浅拷贝。浅拷贝只复制对象本身,对于对象引用的其他对象不进行复制。 深拷贝则将对象与引用对象,全部进行拷贝。最简单的深拷贝就是自己new一个对...

Java常用的几种属性拷贝工具类使用总结

Java属性拷贝工具类使用总结对项目中经常使用的属性拷贝工具类进行总结:org.apache.commons.beanutils.BeanUtilsorg.apache.commons.beanuti...

④ JAVA IO—拷贝(java拷贝文件夹到另外一个文件夹)

一、 传统的IO1. 数据由磁盘拷贝到内核空间(DMA),再由内核空间拷贝到用户空间(JVM)2. 用户可能会对拷贝进来的数据进行操作3. 数据从用户空间拷贝到内核空间(JVM),再通过内核空间将数据...

几种实体拷贝方式实战(拷贝如何使用)

背景我们有这样一个场景,有一个StudentDto类,还有一个StudentVo类@Datapublic class StudentDto { private String id; private S...