Spring Cloud Stream消费者反序列化异常:[B无法转换为QueueObject类
我之前也踩过一模一样的坑!看你的代码,Jackson的类型区分注解(@JsonTypeInfo、@JsonSubTypes)都配置得没问题,生产者也手动设置了application/json的Content-Type,但消费者还是把消息当成byte[](也就是报错里的[B)处理,核心原因是Spring Cloud Stream的消费者没有正确绑定JSON反序列化器,默认用了字节数组的处理逻辑。
下面是针对性的解决步骤,结合你的代码来调整:
1. 强制消费者使用JSON反序列化器(关键配置)
Spring Cloud Stream的Kafka binder默认会用ByteArrayDeserializer,所以必须在配置文件里给你的消费者绑定指定JSON反序列化器,同时配置信任包和类型映射。
比如在application.yml里添加:
spring: cloud: stream: # 配置Kafka消费者的反序列化器 kafka: bindings: gatekeeperIn0-in-0: consumer: value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer properties: # 信任你的QueueObject所在的包,防止反序列化安全限制 spring.json.trusted.packages: in.co.nmsworks.rca.sa.inv.commons # 映射你的@JsonSubTypes的name到实际类路径 spring.json.type.mapping: SimpleQObject:in.co.nmsworks.rca.sa.inv.commons.SimpleQueueObject # 绑定消费者的目标主题和内容类型 bindings: gatekeeperIn0-in-0: destination: reconTrigger-out-0 content-type: application/json
2. 确保生产者用JSON序列化消息
你已经用MessageBuilder设置了Content-Type,但最好再给生产者绑定也明确配置content-type,避免Spring用默认的Java序列化(虽然你的类实现了Serializable,但我们要的是JSON):
spring: cloud: stream: bindings: reconTrigger-out-0: content-type: application/json
3. 验证Jackson类型信息是否正确序列化
有时候Jackson可能因为ObjectMapper的配置问题,没有把type字段写入JSON。你可以在生产者发送前手动序列化测试,确认type字段存在:
// 在addInBuilderQueue方法里加这段测试 @Autowired private ObjectMapper objectMapper; private void addInBuilderQueue(InventoryDataBuilderQueueObject inventoryDataBuilderQueueObject) { SimpleQueueObject objs = new SimpleQueueObject(InventoryOperationType.ADD, false); // 测试序列化 try { String json = objectMapper.writeValueAsString(objs); log.info("序列化后的JSON: {}", json); } catch (JsonProcessingException e) { log.error("序列化失败", e); } // 发送消息 streamBridge.send("reconTrigger-out-0", MessageBuilder.withPayload(objs) .setHeader(MessageHeaders.CONTENT_TYPE, "application/json") .build()); }
正常输出应该包含"type":"SimpleQObject"这个属性,如果没有,说明你用的ObjectMapper没有自动识别@JsonTypeInfo注解,改用Spring容器提供的ObjectMapper bean即可(它会自动处理Jackson的类型注解)。
4. 避免泛型擦除的影响
函数式消费者Consumer<QueueObject>理论上可以被Spring正确识别类型,但如果还是有问题,可以改成接收Message<QueueObject>,明确获取payload:
@Bean public Consumer<Message<QueueObject>> gatekeeperIn0() { return msg -> { QueueObject payload = msg.getPayload(); log.info("Received from RECON: {}", payload); // 后续逻辑 }; }
5. 排除Java序列化的干扰
你的类实现了Serializable,但现在我们用JSON序列化,这个接口其实可以暂时去掉(或者保留,但必须确保配置里没有用JavaSerializer),防止Spring误触发Java序列化逻辑。
按照上面的步骤配置后,消费者应该就能正确把JSON字节数组反序列化成SimpleQueueObject实例,不会再出现[B cannot be cast to QueueObject的错误了。
内容来源于stack exchange

