《一次静态 ObjectMapper 引发的 RocketMQ 消费异常排查》
静态 ObjectMapper 引发的 RocketMQ 消费异常
- 一、问题背景
 - 二、问题原因
 - 三、思考与采取措施
 - 四、根本原因(Spring 注入 & 静态变量 & RocketMQ 消费线程)
 - 五、解决方案
 - 六、总结
 
一、问题背景
在工作中日常使用 RocketMQ 作为消息队列进行异步处理时,发现一个很奇怪的现象:
- 消费者注册成功,控制台无报错 ✅
 - 消息发送正常✅
 - 但是就是收不到消息 ,没有任何异常的日志❌
 
二、问题原因
// 问题代码
private static final ObjectMapper objectMapper = new ObjectMapper();@Overridepublic void onMessage(String msg) {try {MyDTO dto = objectMapper.readValue(msg, MyDTO.class);log.info("Received and parsed message: {}", dto);// TODO: process dto} catch (Exception e) {log.error("Failed to deserialize message: {}", msg, e);}} 
现象描述:
- 消费方法 onMessage 从未被触发
 - RocketMQ 控制台看消息已经成功发送
 - 本地没日志也没报错
 
三、思考与采取措施
排查顺序:
- 检查 topic 和 group 是否一致
 - 检查是否开启消费(listener 生效)
 - 查看消息轨迹,确认消息已投递
 - 添加日志发现 onMessage 根本没有触发
 - 怀疑是反序列化失败,但是catch 里面也没有打日志出来
 - objectMapper 换成 @Autowired,结果立刻恢复正常
 
四、根本原因(Spring 注入 & 静态变量 & RocketMQ 消费线程)
- static final ObjectMapper 手动 new 出来的,不受 Spring 管理
 - Spring 注入的 @Autowired ObjectMapper 是配置完整、统一管理的
 - 如果反序列化失败,RocketMQ 默认不会抛出可见异常,尤其在异步线程中可能被吞掉
 - static 变量初始化早于 Spring 生命周期,可能没准备好就被调用
 
五、解决方案
- 不要手动 new ObjectMapper,使用 Spring 注入
 
@Autowired
private ObjectMapper objectMapper;
 
六、总结
这个问题看似“像是线程问题”,实则是Spring 生命周期 + 组件注入机制的组合坑。
 教训:当 Spring 项目中用到第三方组件(如 ObjectMapper)时,优先用 Spring 提供的注入,而非自己手动 new!
