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

Spring Boot集成ActiveMQ:消息转换异常与序列化问题解析

ActiveMQ消息序列化/反序列化问题排查与最佳实践

问题背景

负责两个模块:Java EE的KafkaRest生产者、Spring Boot的Segmenter消费者。在Spring Boot消费ActiveMQ队列消息时遇到一系列序列化错误:

  1. 初始错误:MessageConversionException,提示消息中找不到_type属性
  2. 尝试为生产者添加_type后,出现新的反序列化错误,包括ClassNotFoundException和类型转换错误

修改后的KafkaRest生产者代码片段

public void sendToSegmenter(PrepareStreamVo prepareStreamVo) throws JMSException {
    String json = null;
    try {
        json = JsonUtil.getJson(prepareStreamVo);
        BytesMessage bytesMessage = getSession().createBytesMessage();
        bytesMessage.writeBytes(json.getBytes());
        try {
            getProducer().send(bytesMessage);
        } catch (JMSException e) {
            logger.error("fail send message to segmenter with producer message={} error={} ", json, e.getMessage());
            getFallBackProducer().send(bytesMessage);
        }
    } catch (JMSException e) {
        logger.error("fail send message to segmenter with FallBackProducer message={} error={} ", json, e.getMessage());
        throw e;
    } 
}

错误信息汇总

org.springframework.jms.support.converter.MessageConversionException: Could not find type id property [_type] on message
org.springframework.jms.listener.adapter.ListenerExecutionFailedException:
Listener method ... threw exception; nested exception is org.springframework.jms.support.converter.MessageConversionException:
Failed to resolve type id
[org.sc.oauth2.resource.server.model.PlaceForm]; nested exception is java.lang.ClassNotFoundException:
org.sc.oauth2.resource.server.model.PlaceForm

Second Error After Modification: Cannot deserialize instance of java.lang.String out of START_OBJECT token active mq Cannot convert from [[B] to [org.apache.activemq.command.ActiveMQTextMessage] for String, byte array, Map<String,?>, Serializable object

问题解答

1. _type相关错误的成因及添加后未解决的原因

  • 初始MessageConversionException原因:Spring JMS默认使用MappingJackson2MessageConverter时,会依赖消息的_type属性确定反序列化的目标类。生产者发送的是BytesMessage,既没有在消息属性中嵌入_type标识,也没有在消息体内携带该字段,消费者无法识别字节数组对应的Java类,因此报错。
  • 添加_type后仍报错的原因:大概率是仅在业务JSON中添加了_type字段,但消费者的转换器默认从JMS消息属性读取_type,而非解析JSON内容。另外ClassNotFoundException说明指定的_type对应类org.sc.oauth2.resource.server.model.PlaceForm不在消费者项目的类路径中,或类名/包名配置错误。同时BytesMessage的二进制格式与消费者期望的文本消息处理逻辑不匹配,进一步导致反序列化失败。

2. 改用TextMessage解决问题的原因

  • BytesMessage是二进制消息格式,Spring JMS转换器会默认按字节数组解析,而实际内容是JSON字符串,转换器无法自动识别这是JSON格式的文本,导致类型转换冲突。
  • 改用TextMessage后,消息会被标记为文本类型,消费者的MappingJackson2MessageConverter会直接读取消息体的字符串内容,按JSON格式反序列化。此时只要JSON结构与目标类匹配,或正确配置了_type属性(消息属性或自定义转换器从JSON读取),就能完成类型转换,解决字节数组与文本/对象的格式冲突问题。

3. Spring Boot + ActiveMQ消息转换最佳实践

  • 统一消息类型:生产者和消费者尽量使用一致的消息类型,推荐用TextMessage传输JSON格式数据,可读性强且便于调试,避免二进制消息的类型识别问题。
  • 明确配置消息转换器:在Spring Boot消费者中显式配置MappingJackson2MessageConverter,指定类型识别规则:
    @Bean
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
        // 用别名映射替代类全限定名,避免跨模块类依赖问题
        converter.setTypeIdMappings(Map.of(
            "prepareStream", PrepareStreamVo.class
        ));
        return converter;
    }
    
  • 避免依赖类全限定名:不要直接用类的全路径作为_type值,改用别名并通过类型映射关联实际类,避免跨模块类路径不兼容问题。
  • 规范消息属性设置:如果使用BytesMessage,需在消息属性中设置_type,而非放在业务JSON内;如果使用TextMessage,可选择在消息属性添加_type,或让转换器直接根据目标方法参数类型反序列化(无需_type)。
  • 完善异常处理与日志:生产者和消费者都要捕获消息发送/消费的异常,记录完整的消息内容和错误栈,便于排查序列化问题。
  • 保证序列化兼容性:确保生产者和消费者使用的JSON序列化工具(如Jackson)版本一致,且类的字段结构、序列化注解保持兼容(比如字段名一致、存在无参构造器)。

内容的提问来源于stack exchange,提问作者Sajjad Serajzadeh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 12:22:39