You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RabbitMQ跨服务传对象因包名不同引发ClassNotFoundException问题

问题描述

两个服务通过RabbitMQ互相发送基于Jackson多态序列化的对象,父类定义如下:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Subclass.class, name = "Subclass")
})
public abstract class ParentClass implements Serializable {...}

发送端生成的RabbitMQ消息包含以下内容:

__TypeId__: sending.service.package.Subclass
content_encoding:   UTF-8
content_type:   application/json
Payload
{"type":"Subclass", etc}

接收端存在同名但包名不同的Subclass,接收消息时触发sending.service.package.Subclass的ClassNotFoundException。预期中Jackson的@JsonTypeInfo和@JsonSubTypes应该使用Payload中的type字段匹配子类,但实际消息转换器却使用了RabbitMQ消息头中的__TypeId__字段。接收端监听代码如下:

@RabbitListener(queues = ParentClassConfig.QUEUE_NAME)
public void handleParentClass(final @Payload ParentClass parentClass) {
    // 处理逻辑
}

已尝试通过ObjectMapper注册子类、开启NON_CONCRETE_AND_ARRAYS类型默认处理,但均无效。

问题原因与解决方案

问题原因

Spring AMQP默认使用的Jackson2JsonMessageConverter会优先读取RabbitMQ消息头中的__TypeId__字段来确定反序列化目标类型,而非完全依赖Jackson自身的@JsonTypeInfo配置。发送端发送消息时,该转换器会自动将发送类的全限定类名写入__TypeId__头,接收端尝试加载这个跨包的类时就会抛出ClassNotFoundException。

解决方案

1. 禁用__TypeId__头的类型解析

自定义Jackson2JsonMessageConverter,通过设置setTypeIdPropertyName(null)让转换器忽略消息头的类型信息,完全依赖Payload中的type字段进行Jackson多态匹配:

@Bean
public MessageConverter jackson2JsonMessageConverter() {
    Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
    // 禁用消息头的类型解析,转而使用Jackson的@JsonTypeInfo配置
    converter.setTypeIdPropertyName(null);
    return converter;
}

也可以将typeIdPropertyName设置为你在@JsonTypeInfo中定义的type字段,让转换器同步使用该字段,但直接禁用头解析更贴合你的需求。

2. 确保两端Jackson多态配置一致

发送端和接收端的ParentClass上的@JsonSubTypes配置必须完全匹配:

  • 两端的name值要一致(比如都为"Subclass")
  • 各自的value指向自身包下的Subclass类

这样Jackson在解析Payload中的type字段时,能正确映射到接收端本地的子类。

内容的提问来源于stack exchange,提问作者Wouter Zelle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 21:35:02