【问题标题】:How to keep JMS Headers after Unmarshalling their Xml payload in spring integration在春季集成中解组其 Xml 有效负载后如何保留 JMS 标头
【发布时间】:2026-02-05 21:20:05
【问题描述】:

我正在使用 Jmeter 发送带有 XML 有效负载和一些自定义标头 contentType 的 JMS 消息,其值为 text/jsontext/xml

我的 Spring 集成配置如下所示:

<jms:message-driven-channel-adapter channel="jmsInChannel" destination-name="queue.demo" connection-factory="jmsConnectionFactory1" />
<int:channel id="jmsInChannel" />

<int:header-value-router input-channel="jmsInChannel" header-name="contentType" default-output-channel="nullChannel">
    <int:mapping value="text/json" channel="jsonTransformerChannel" />
    <int:mapping value="text/xml" channel="xmlTransformerChannel" />
</int:header-value-router>

到目前为止,一切正常,消息已成功路由到各自的转换器。

我的问题是,当它是一个 XML 有效负载时,我首先使用了 JAXB Unmarshaller 以及 http://www.springframework.org/schema/integration/xml 提供的 &lt;ixml:unmarshalling-transofmer ../&gt;

我可以得到payload,但是之后它不能将消息作为JMS消息处理,它变成了一个纯粹的POJO。所以我丢失了标头,如果不序列化 POJO 就无法使用&lt;int: ../&gt; 组件,这不是我想要实现的。

我找到了一种解决方法,我在 java 中定义了自己的 Unmarshalling bean,如下所示:

<int:channel id="xmlTransformerChannel" />
<int:transformer input-channel="xmlTransformerChannel" ref="xmlMsgToCustomerPojoTransformer" output-channel="enrichInChannel" />

方法:

@SuppressWarnings("rawtypes")
public Message transform(String message) {

    logger.info("Message Received \r\n" + message);

    try {
        MyModel myModel = (MyModel) unmarshaller.unmarshal(new StreamSource(new StringReader(message)));
        return MessageBuilder.withPayload(myModel).build();

    } catch (XmlMappingException e) {
        return MessageBuilder.withPayload(e).build();
    } catch (Exception e) {
        return MessageBuilder.withPayload(e).build();
    }
}

我可以成功地将消息作为 Spring 集成消息处理,但我丢失了原始的自定义 JMS 标头。

相比之下,我必须做的就是转换 json 有效负载并保持消息格式并保留我的自定义标头是这个 xml 配置:

<int:channel id="jsonTransformerChannel" />
<int:json-to-object-transformer input-channel="jsonTransformerChannel" output-channel="enrichInChannel" type="com.alrawas.ig5.MyModel" />

我的问题是,如何在解组 xml 有效负载后保留原始的自定义 JMS 标头?

更新:

我确实尝试过以这种方式编写 xml 转换器,将 Message 作为输入而不是仅使用字符串,在此方法中它没有抛出异常,但在路由阶段稍后会抛出异常

public Message<?> transform(Message<String> message) {

    logger.info("Message Received \r\n" + message);

    try {
        MyModel myModel = (MyModel) unmarshaller.unmarshal(new StreamSource(new StringReader(message.getPayload())));
        return (Message<MyModel>) MessageBuilder.withPayload(myModel).copyHeaders(message.getHeaders()).build();

    } catch (XmlMappingException e) {
        return MessageBuilder.withPayload(e).build();
    } catch (Exception e) {
        return MessageBuilder.withPayload(e).build();
    }
}

我在稍后在此流程中使用的组件中遇到了问题:

<int:object-to-json-transformer input-channel="outr" output-channel="outch" />
<int:router method="route" input-channel="outch" default-output-channel="nullChannel">
    <bean class="com.alrawas.ig5.MyCustomRouter" />
</int:router>

在我的路由方法中抛出Cannot cast String to com.alrawas.ig5.MyModel 异常:

public class MyCustomRouter {

    public String route(Message<MyModel> myModel) {

    Integer tenNumber = myModel.getPayload().getNumber(); // <-- Cast Exception here
    System.out.println(myModel);
    return (tenNumber % 10 == 0) ? "stayLocal" : "goRemote";

    }
}

此 Cast Exception 仅在解组 xml 后发生,JSON 有效负载工作正常,不会丢失标头或抛出 cast 异常。

更新:

在下面查看我的答案:contentType was not really a custom header

【问题讨论】:

    标签: java spring-integration unmarshalling spring-jms


    【解决方案1】:

    当您开发自定义 transformer 时,您需要记住,返回 Message&lt;?&gt; 会导致您完全控制其内容。

    当转换器函数返回 Message 时,它不会填充任何请求标头。

    因此,您的 public Message transform(String message) { 必须期望 Message 作为输入,并且您需要复制请求消息中的所有标头以回复消息。 MessageBuilder上有相应的方法。

    另一方面,完全不清楚为什么需要在此处返回 Message,因为 Spring 集成中的所有内容在发送到输出通道之前都将被包装到 Message

    【讨论】:

    • 我更新了我的问题,除了更新后的问题,我确实尝试让变压器返回 MyModel,但后来路由器中仍然出现 ClassCastException。
    • 但是您必须在该路由器之前使用 JSON 转换器。因此,您的 pojo 将转换为 String。这就是你想要的吗?
    • 我去制作我自己的 xml 解组器转换器而不是 &lt;ixml:unmarshalling-transofmer ../&gt;,因为当我尝试使用我的 &lt;int:object-to-json-transformer../&gt; 后跟我的自定义 &lt;int:router ../&gt; 时,当我在路由器中向 MyModel ClassCastException 抛出一个字符串时试图访问模型。
    • 先使用json,使用json-to-object后使用object-to-json,没有导致路由器后面抛出ClassCastException。我想知道为什么当有效载荷最初是 xml 时,object-to-json 转换成功了,所以下一个路由器不能像原来的 json 有效载荷那样进行转换
    • 不是真的,我想我应该专门针对我在上一条评论中描述的内容发布一个新问题,这就是我使用这种垃圾方法深入研究这个问题的最初原因。
    【解决方案2】:

    采取:

    ClassCastException 稍后在最后一个路由器中发生,因为我将自定义标头命名为 contentType。这是内部使用的默认 jms 标头。当我将它的值更改为text/xml 时,最后一个路由器String route(Message&lt;MyModel&gt; myModel) 试图将json 转换为MyModel,它失败了,因为标头不再是application/json,而是text/xml。这导致了 ClassCastException。

    所以我摆脱了自定义 xml 解组逻辑 bean。我重命名了我的自定义标题。并使用&lt;ixml:unmarshalling-transformer ../&gt;

    它使用 xml 配置工作,无需额外的自定义 java bean。

    【讨论】: