
在 apache camel 路由中,`unmarshal` 操作会默认覆盖消息体,导致原始 json 字符串丢失;可通过 `setproperty()` 将原始消息暂存为 exchange 属性,在后续处理器中安全读取或恢复。
在基于消息驱动的集成场景中(如从 JMS、Kafka 或 ActiveMQ 队列接收 JSON 数据),我们常需将原始 JSON 字符串反序列化为 Java 对象以便业务逻辑处理。但 Apache Camel 的 unmarshal() 是有状态的转换操作:它会直接替换 Exchange.getIn().getBody(),原始输入(如字符串形式的 JSON)一旦被覆盖便无法通过 body() 直接获取。
解决该问题的核心思路是:在反序列化前,主动捕获并持久化原始消息体。Camel 提供了轻量、线程安全的 Exchange.setProperty() 机制,可将任意对象(如 String)存入当前 Exchange 的属性空间,生命周期贯穿整个路由过程。
以下是一个典型且健壮的实现方案:
from("jms:queue:example")
.routeId("receiveExampleMessage")
// 确保输入为 String 类型(避免字节数组或 InputStream 导致后续 getProperty 失败)
.convertBodyTo(String.class)
// 将原始 JSON 字符串存入名为 "originalBody" 的 Exchange 属性
.setProperty("originalBody", simple("${body}"))
// 执行 JSON 反序列化(例如使用 JacksonDataFormat)
.unmarshal(new JacksonDataFormat(ExamplePojo.class))
// 此时 body() 返回 ExamplePojo 实例,而原始 JSON 仍可通过属性访问
.log("Received POJO: ${body} | Original JSON: ${exchangeProperty.originalBody}")
.process(exchange -> {
String originalJson = exchange.getProperty("originalBody", String.class);
ExamplePojo pojo = exchange.getMessage().getBody(ExamplePojo.class);
// 在此处混合使用原始 JSON(如审计、签名、重试日志)与结构化对象
log.info("Processing {} with raw payload: {}", pojo.getId(), originalJson);
})
// 如需恢复原始消息体(例如转发至下游非 Java 系统),可重新设回
.setBody().exchangeProperty("originalBody");✅ 关键注意事项:
- 务必先 convertBodyTo(String.class):若原始消息为 byte[] 或 InputStream,直接 getProperty("originalBody") 可能返回不可读内容或空值;显式转为 String 可确保语义清晰、兼容性强。
- 属性名应具唯一性:避免与 Camel 内置属性(如 CamelFileName、CamelRedelivered)冲突,推荐使用带业务前缀的名称(如 "rawJsonPayload")。
- 内存与性能考量:对于超大 JSON 消息(如 >10MB),频繁复制字符串可能增加 GC 压力;此时可考虑仅保存摘要(如 SHA-256)、或改用 header() 存储元数据,原始体通过 StreamCache 等方式按需重放。
- 错误处理增强建议:可在 onException() 中记录 exchange.getProperty("originalBody"),便于排查反序列化失败原因(如格式错误、字段缺失)。
总结而言,Camel 的 Exchange 属性机制是解耦“数据解析”与“原始上下文保留”的标准实践。掌握这一模式,不仅能解决 unmarshal 后访问原始消息的问题,也为实现幂等校验、变更追踪、协议桥接等高级集成能力奠定基础。










