美文网首页
类加载器不一致导致的转换异常

类加载器不一致导致的转换异常

作者: luncene_e110 | 来源:发表于2021-03-26 15:59 被阅读0次

    最近在一个项目中使用rocketmq发送消息,在消费消息时,反序列化是出现一个报错,最开始以为是alibaba的fastjson报的错。

    详细报错为:

    java.lang.ClassCastException: com.dtyunxi.amdu.dms.common.event.clue.dto.PlatformClueMsgDto cannot be cast to com.dtyunxi.amdu.dms.common.event.clue.dto.PlatformClueMsgDto

    at com.alibaba.fastjson.serializer.ASMSerializer_14_PlatformClueMsgDto.write(Unknown Source)

    at com.alibaba.fastjson.serializer.JSONSerializer.writeWithFieldName(JSONSerializer.java:314)

    at com.alibaba.fastjson.serializer.ASMSerializer_11_MessageVo.write(Unknown Source)

    at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:285)

    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:731)

    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:669)

    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:634)

    at com.dtyunxi.huieryun.mq.provider.rocket.RocketConsumer$1.consumeMessage(RocketConsumer.java:205)

    at org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService$ConsumeRequest.run(ConsumeMessageConcurrentlyService.java:411)

    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)

    at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)

    at java.util.concurrent.FutureTask.run(FutureTask.java)

    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

    从原文看是castException。检查包全路径,没有区别。初步以为是fastjson的bug。

    后来通过类加载器,在consumer端查看本地加载的PlatformClueMsgDto,和消息体力的body消息体的加载器不一致,导致转换失败。

    如下图

    图1 图2

    platformClueMsgDto是在本地加载的dto对象,而message.getData()也是platClueMsgDto(注:MessageVo是由第三方的jar包封装的对象)。

    从截图看本地包的platformClueMsgDto的类加载器对象是RestartClassLoader,而MessageVo里的platClueMsgDto的类加载器是AppClassLoader。

    一般情况下,我们的类加载器按理都应该是AppClassLoader。

    所以我们来看看RestartCalssLoader类加载器是什么。

    RestartCalssLoader是由Spring-boot-devtools引入的类加载器。

    整个启动流程大概:

    main() -> SpringApplication.run() -> SpringApplicationRunListeners.starting() ->

    SimpleApplicationEventMulticaster.multicastEvent() RestartApplicationListener.onApplicationEvent() ->

    RestartApplicationListener.onApplicationStartingEvent() -> Restarter.initialize()

    由此我们看到是由实现ApplicationListener的RestartApplicationListener完成对Restarter的初始化的,而此是传入的Thread正是main线程,此线程的ContextClassLoader便是AppClassLoader,所以经过过滤后就将只有类似于 file:/projectPath/projectName/module/target/classes这样的属于此项目代码的URL,所以此类加载器不负责加载第三方jar包的类文件。

    ClassLoader不一致的解决方法

    1.最粗暴的方式就是直接去除依赖 Spring-boot-devtools

    2.如果确实想要热部署功能,Springboot也提供了配置,没错就是上文提到的过滤URLs时使用DevToolsSettings读取META-INF/spring-devtools.properties配置文件进行纳入、排查,你可以自己新建 spring-devtools.properties 文件,配置上需要此自定义类加载器负责来加载的正则表达式,形式如(exclude表示排查、include表示纳入):

    restart.exclude.spring-boot=/spring-boot/target/classes/

    restart.exclude.spring-boot-devtools=/spring-boot-devtools/target/classes/

    restart.exclude.spring-boot-starters=/spring-boot-starter-[\\w-]+/

    restart.include.commons-pool2=/org/apache/commons/commons-pool2/2.4.3/commons-pool2-2.4.3.jar

    相关文章

      网友评论

          本文标题:类加载器不一致导致的转换异常

          本文链接:https://www.haomeiwen.com/subject/wswjhltx.html