同一类出现ClassCastException异常排查(Spring Boot DevTools场景)
从报错日志能看到核心问题:
Caused by: java.lang.ClassCastException: class com.exceltic.commons.api.model.datacollector.accounts.CompletedAccountDTO cannot be cast to class com.exceltic.commons.api.model.datacollector.accounts.CompletedAccountDTO (com.exceltic.commons.api.model.datacollector.accounts.CompletedAccountDTO is in unnamed module of loader 'app'; com.exceltic.commons.api.model.datacollector.accounts.CompletedAccountDTO is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @2db52090)
Java里判断两个类是否为同一类型,不仅看全限定类名,还要看加载它的类加载器。这里CompletedAccountDTO被两个不同的类加载器加载了:一个是系统类加载器('app' loader),另一个是Spring Boot DevTools的RestartClassLoader,所以它们在JVM里是完全不同的类型,哪怕名字完全一样,哪怕是隐式的类型转换(比如映射框架内部的转换)都会抛出ClassCastException。
为什么会触发这个问题?
你用了Spring Boot DevTools的热重启功能,它的RestartClassLoader会单独加载应用代码,实现修改代码后快速重启。但Kafka的反序列化过程可能用了系统类加载器加载CompletedAccountDTO,而你的DTO与实体映射逻辑(比如AccountsMapperImpl里的代码)是被RestartClassLoader加载的,这就导致两个类实例属于不同类加载器下的类型,映射时触发类型转换异常。
解决方法
- 排除DTO包到DevTools重启范围外:在
application.properties或application.yml里添加配置,让DTO类始终由系统类加载器加载:spring.devtools.restart.exclude=com/exceltic/commons/api/model/datacollector/accounts/** - 禁用DevTools热重启:如果不需要热重启功能,直接关闭它,在配置文件里设置:
或者移除pom.xml(或build.gradle)里的DevTools依赖。spring.devtools.restart.enabled=false - 统一类加载器:自定义Kafka反序列化器,使用当前线程的上下文类加载器加载
CompletedAccountDTO,确保和映射逻辑用同一个类加载器。示例代码如下:
然后在Kafka消费者配置里指定这个自定义反序列化器。public class CustomDeserializer implements Deserializer<CompletedAccountDTO> { @Override public CompletedAccountDTO deserialize(String topic, byte[] data) { ObjectMapper objectMapper = new ObjectMapper(); try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); return objectMapper.readValue(data, classLoader.loadClass("com.exceltic.commons.api.model.datacollector.accounts.CompletedAccountDTO")); } catch (IOException | ClassNotFoundException e) { throw new SerializationException("Failed to deserialize CompletedAccountDTO", e); } } }
内容的提问来源于stack exchange,提问作者Miguel Ángel Cantarero Ortega

