最近使用 springboot
及 netty
进行中间件开发,其中出现很奇怪的问题,同样的 Class
在做 foreach
的过程中出现 ClassCastException
,具体异常如下:
java.lang.ClassCastException: com.xxx..entities.SyncSubscribe cannot be cast to com.xxx.entities.SyncSubscribe
at java.util.ArrayList.forEach(ArrayList.java:1257) ~[na:1.8.0_191]
at com.xxx.services.impl.SyncServiceImpl.sendPublishMessage(SyncServiceImpl.java:252) ~[classes/:na]
at com.xxx.services.impl.SyncServiceImpl.processPublish(SyncServiceImpl.java:208) ~[classes/:na]
at com.xxx.server.handlers.MqttHandler.channelRead0(MqttHandler.java:45) ~[classes/:na]
at com.xxx.server.handlers.MqttHandler.channelRead0(MqttHandler.java:23) ~[classes/:na]
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) ~[netty-all-4.1.43.Final.jar:4.1.43.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) [netty-all-4.1.43.Final.jar:4.1.43.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) [netty-all-4.1.43.Final.jar:4.1.43.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) [netty-all-4.1.43.Final.jar:4.1.43.Final]
at com.xxx.server.handlers.MqttExceptionHandler.channelRead(MqttExceptionHandler.java:30) [classes/:na]
在调试过程中,发现如下现象:
同一类不同ClassLoader问题如上图所示,两个类名称完全一样,均为自定义实体,当两个类型在不同的 ClassLoader
中,导致 java.lang.ClassCastException
问题。
项目启动时加载项目中的类使用的加载器都是
org.springframework.boot.devtools.restart.classloader.RestartClassLoader
而从 session
取出来的对象的类加载器都是 sun.misc.Launcher.AppClassLoader
很明显会导致类型转换异常,原来Spring的dev-tools为了实现重新装载class自己实现了一个类加载器,来加载项目中会改变的类,方便重启时将新改动的内容更新进来。因此,导致上述内容产生 ClassCastException
。
解决方案
共有两个解决方案,如下所示:
-
不使用
spring-boot-devtools
做热部署 -
在
resources
目录下面创建META-INF
文件夹,然后创建spring-devtools.properties
文件,文件加上类似下面的配置:
restart.exclude.xxxcommonlibs=/xxxx-[\w-]+.jar
restart.include.projectcommon=/xxxx-[\w-]+.jar
即,将其依赖库均导入到 devtools
库中
网友评论