当前位置:首页 > 编程笔记 > 正文
已解决

SpringBoot SerializationUtils克隆(反序列化) 类加载器不一致问题(ClassCastException)

来自网友在路上 164864提问 提问时间:2023-10-30 18:19:12阅读次数: 64

最佳答案 问答题库648位专家为你答疑解惑

问题分析

在SpringBoot中使用 org.apache.commons.lang.SerializationUtils.clone 方法时,发现克隆出来的类强转对应类时发生类型不一致的错误,经过检测发现两个看似相同的类的类加载器不一致

场景

在这里插入图片描述

报错信息

java.lang.ClassCastException: com.tianqiauto.tis.pc.dingdanyupai.po.PrePoint cannot be cast to com.tianqiauto.tis.pc.dingdanyupai.po.PrePoint

检测信息

在这里插入图片描述

在这里插入图片描述

解决方法分析

既然发现类加载器不一致,那么需要找到类反序列化时的类加载器是如何指定得
在这里插入图片描述

深入SerializationUtils.clone方法时发现内部是通过jdk的反序列化类ObjectInputStream将字节码转为对象得
在这里插入图片描述

在这里插入图片描述

发现返回对象是由cons创建的,cons 是一个Constructor,那么需要判断cons是在哪里生成的,从而推断出类加载器的生成依据,而cons存在于ObjectStreamClass,进入ObjectStreamClass desc = readClassDesc(false);判断cons何时赋值
在这里插入图片描述

发现执行完desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));这段代码时,cons有值,进入initNonProxy方法中
在这里插入图片描述

发现最终指向Caches.localDescs一个map中
在这里插入图片描述

localDescs是一个全局静态变量,所以需要知道在什么地方添加值的
在这里插入图片描述

打断点debug发现在序列化的时候会new ObjectStreamClass(cl)并放在localDescs里,所以进入new ObjectStreamClass(cl)的方法里,找cons的来源
在这里插入图片描述

发现cons是由Class<?> cl生成,也就是说cons的类加载器信息是由Class<?> cl的类加载器决定的,反向查找cl的加载
在这里插入图片描述

发现cl的类加载信息由latestUserDefinedLoader(),查阅资料发现,latestUserDefinedLoader()会根据栈帧信息查找第一个非根类加载器或扩展类加载器,而SerializationUtils属于ApplicationClassLoader加载的范围,所以SerializationUtils.clone(point)返回的对象是由ApplicationClassLoader加载
在这里插入图片描述

解决方案

方案一(推荐)

SerializationUtils.clone中的方法复制到项目中

public class TestClassLoaderController {private static PrePoint point = new PrePoint();@GetMapping("/testClassLoader")public AjaxResult testClassLoader() {PrePoint deserialize = (PrePoint) cloneObject(point);return null;}private static Object cloneObject(PrePoint point) {ByteArrayOutputStream baos = new ByteArrayOutputStream(512);serialize(point, baos);byte[] bytes = baos.toByteArray();return deserialize(bytes);}public static Object deserialize(byte[] objectData) {if (objectData == null) {throw new IllegalArgumentException("The byte[] must not be null");}ByteArrayInputStream bais = new ByteArrayInputStream(objectData);return deserialize(bais);}public static Object deserialize(InputStream inputStream) {if (inputStream == null) {throw new IllegalArgumentException("The InputStream must not be null");}ObjectInputStream in = null;try {// stream closed in the finallyin = new ObjectInputStream(inputStream);return in.readObject();} catch (ClassNotFoundException ex) {throw new SerializationException(ex);} catch (IOException ex) {throw new SerializationException(ex);} finally {try {if (in != null) {in.close();}} catch (IOException ex) {// ignore close exception}}}public static void serialize(Serializable obj, OutputStream outputStream) {if (outputStream == null) {throw new IllegalArgumentException("The OutputStream must not be null");}ObjectOutputStream out = null;try {// stream closed in the finallyout = new ObjectOutputStream(outputStream);out.writeObject(obj);} catch (IOException ex) {throw new SerializationException(ex);} finally {try {if (out != null) {out.close();}} catch (IOException ex) {// ignore close exception}}}
}

方案二

关闭热加载

spring:devtools:restart:enabled: false

或者

@SpringBootApplication
public class SSMPApplication {public static void main(String[] args) {System.setProperty("spring.devtools.restart.enabled","false");SpringApplication.run(SSMPApplication.class);}
}

方案三

移除热加载的jar包

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
</dependency>
查看全文

99%的人还看了

猜你感兴趣

版权申明

本文"SpringBoot SerializationUtils克隆(反序列化) 类加载器不一致问题(ClassCastException)":http://eshow365.cn/6-27960-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!