探寻泛型方法ClassCastException元凶

作者:王帅景 来源:服务端思维

一、现象

本地开发环境在开发新功能的过程中突然出现了诡异的ClassCastException,之所以称之为诡异,是因为出现了对象强转自身所属类异常。 发生的场景:项目首次接入memcache,在通过泛型方法取值时,虽然取到的值和接收的值是同一个类,但是却出现强转异常。(自己无法强转自己 ⊙﹏⊙∥∣°) 抛出异常:java.lang.ClassCastException: com.zhqy.bean.Person cannot be cast to com.zhqy.bean.Person

二、问题排查

1、怀疑泛型方法有问题,但是方法简单到无法怀疑

2、怀疑是工具方法中的泛型有问题,导致无法正确转换 使用Junit和main方法运行具体的工具方法,发现结果一直是正确的,未出现一次ClassCastException。

三、问题解决

一般的排查手段已经无法解决问题,最后还是同事「张帆」提出,可能是因为spring-boot-devtools热部署功能使用了自定义ClassLoader导致的问题。

验证理论的过程如下:

1、先简单准备自定义classLoader的实例代码,代码如下:

2、根据抛出的强转异常,将示例代码的第一句进行如下修改:

根据修改后obj instanceof Person的结果可知,obj不是Person的对象。(很蛋疼的结果,因为理论上obj确实是Person类的对象) 在这种情况下,分析示例代码的字节码得知,instanceof结果为false和泛型方法出现强转异常,是因为泛型方法仅仅只是省去人为指定强转类型而已,并不是没有强转的那一步。详见字节码文件内容:

四、结论

在示例代码中,虽然被强转对象和接收的对象是同一个,但是因为classLoader不是同一个,在CHECKCAST时就会抛出ClassCastException异常。 解决办法

参考文档: INSTANCEOF、CHECKCAST关键字解释 [张振阳] spring-boot-devtools 不同ClassLoader引起的问题 spring-boot-devtools文档,Customizing the Restart Classloader部分