跳到主要内容
版本:7.0.2

消息处理器链

DeepSeek V3 中英对照 Message Handler Chain

MessageHandlerChainMessageHandler 的一种实现,它可以配置为单一的消息端点,而实际上委托给其他处理程序链,例如过滤器、转换器、拆分器等。当多个处理程序需要以固定的线性顺序连接时,这可以带来更简单的配置。例如,在其他组件之前提供转换器是相当常见的。同样,当您在链中的其他组件之前提供过滤器时,您实际上创建了一个选择性消费者。在这两种情况下,该链只需要一个 input-channel 和一个 output-channel,无需为每个单独的组件定义通道。

备注

MessageHandlerChain 主要是为 XML 配置设计的。对于 Java DSL,IntegrationFlow 的定义可以被视为一个链式组件,但它与本章下面描述的概念和原则无关。更多信息请参阅 Java DSL

提示

Spring Integration 的 Filter 提供了一个布尔属性:throwExceptionOnRejection。当你在同一个点对点通道上配置多个具有不同接收条件的选择性消费者时,应该将此值设为 true(默认值为 false),这样分发器就能知道消息被拒绝,从而尝试将消息传递给其他订阅者。如果不抛出异常,即使过滤器已丢弃消息以阻止进一步处理,在分发器看来消息也像是已成功传递。如果你确实想要“丢弃”消息,过滤器的 discard-channel 可能会很有用,因为它确实为你提供了对丢弃的消息执行某些操作的机会,例如将其发送到 JMS 队列或写入日志。

处理程序链简化了配置,同时在内部保持了组件间相同程度的松耦合。如果需要非线性排列,修改配置也轻而易举。

在内部,链被展开为一系列按顺序排列的端点,这些端点通过匿名通道分隔。在链内部,回复通道头信息不会被考虑。只有在最后一个处理器被调用后,生成的消息才会被转发到回复通道或链的输出通道。由于这种设置,除了最后一个处理器外,所有处理器都必须实现 MessageProducer 接口(该接口提供了 setOutputChannel() 方法)。如果 MessageHandlerChain 上的 outputChannel 被设置,那么最后一个处理器只需要一个输出通道。

备注

与其他端点一样,output-channel 是可选的。如果链的末尾有回复消息,则 output-channel 具有优先权。然而,如果未设置 output-channel,链处理器会检查入站消息的回复通道头作为备选方案。

在大多数情况下,您无需自行实现 MessageHandler。下一节将重点介绍链元素对命名空间的支持。大多数 Spring Integration 端点,例如服务激活器和转换器,都适合在 MessageHandlerChain 中使用。

配置链

<chain> 元素提供了一个 input-channel 属性。如果链中的最后一个元素能够生成回复消息(可选),它还支持一个 output-channel 属性。子元素可以是过滤器、转换器、拆分器和服务激活器。最后一个元素也可以是路由器或出站通道适配器。以下示例展示了一个链的定义:

<int:chain input-channel="input" output-channel="output">
<int:filter ref="someSelector" throw-exception-on-rejection="true"/>
<int:header-enricher>
<int:header name="thing1" value="thing2"/>
</int:header-enricher>
<int:service-activator ref="someService" method="someMethod"/>
</int:chain>

在前面的示例中使用的 <header-enricher> 元素会在消息上设置一个名为 thing1、值为 thing2 的消息头。头部增强器是 Transformer 的一种特化形式,它仅操作头部值。你也可以通过实现一个 MessageHandler 来修改头部,并将其配置为 bean 来达到相同效果,但使用头部增强器是更简单的选择。

<chain> 可以被配置为消息流的最后一个“封闭式”消费者。对于这种解决方案,你可以在 <chain> 的末尾放置一个 <outbound-channel-adapter>,如下例所示:

<int:chain input-channel="input">
<int-xml:marshalling-transformer marshaller="marshaller" result-type="StringResult" />
<int:service-activator ref="someService" method="someMethod"/>
<int:header-enricher>
<int:header name="thing1" value="thing2"/>
</int:header-enricher>
<int:logging-channel-adapter level="INFO" log-full-message="true"/>
</int:chain>
important

禁止使用的属性和元素

某些属性,例如 orderinput-channel,不允许在链中使用的组件上指定。对于轮询器子元素也是如此。

对于 Spring Integration 核心组件,XML 模式本身会强制执行其中一些约束。然而,对于非核心组件或您自己的自定义组件,这些约束由 XML 命名空间解析器强制执行,而不是由 XML 模式强制执行。

这些 XML 命名空间解析器约束是在 Spring Integration 2.2 中添加的。如果您尝试使用禁止的属性和元素,XML 命名空间解析器将抛出 BeanDefinitionParsingException

使用 'id' 属性

从 Spring Integration 3.0 开始,如果为链元素指定了 id 属性,则该元素的 bean 名称由链的 id 和元素自身的 id 组合而成。未指定 id 属性的元素不会注册为 bean,但每个元素都会获得一个包含链 idcomponentName。请参考以下示例:

<int:chain id="somethingChain" input-channel="input">
<int:service-activator id="somethingService" ref="someService" method="someMethod"/>
<int:object-to-json-transformer/>
</int:chain>

在前面的例子中:

  • <chain> 根元素的 id 为 'somethingChain'。因此,AbstractEndpoint 实现(根据 input-channel 类型为 PollingConsumerEventDrivenConsumer)的 bean 将此值作为其 bean 名称。

  • MessageHandlerChain bean 获取一个 bean 别名(somethingChain.handler),这允许从 BeanFactory 直接访问此 bean。

  • <service-activator> 不是一个完整的消息端点(它不是 PollingConsumerEventDrivenConsumer)。它是 <chain> 内的一个 MessageHandler。在这种情况下,注册到 BeanFactory 的 bean 名称为 'somethingChain$child.somethingService.handler'。

  • ServiceActivatingHandlercomponentName 采用相同的值,但没有 '.handler' 后缀。它变为 'somethingChain$child.somethingService'。

  • 最后一个 <chain> 子组件 <object-to-json-transformer> 没有 id 属性。其 componentName 基于其在 <chain> 中的位置。在这种情况下,它是 'somethingChain$child#1'。(名称的最后部分是链中的顺序,从 '#0' 开始)。注意,此转换器未在应用程序上下文中注册为 bean,因此它没有 beanName。但是,其 componentName 具有一个对日志记录和其他目的有用的值。

<chain> 元素的 id 属性使其有资格进行 JMX 导出,并且可以在 消息历史 中进行追踪。如前所述,您可以通过使用适当的 bean 名称从 BeanFactory 中访问它们。

提示

<chain> 元素提供显式的 id 属性非常有用,可以简化日志中子组件的标识,并允许从 BeanFactory 等地方访问它们。

从链中调用链

有时,您需要在一个链中嵌套调用另一个链,然后返回并在原始链中继续执行。为了实现这一点,您可以通过包含一个 <gateway> 元素来使用消息网关,如下例所示:

<int:chain id="main-chain" input-channel="in" output-channel="out">
<int:header-enricher>
<int:header name="name" value="Many" />
</int:header-enricher>
<int:service-activator>
<bean class="org.foo.SampleService" />
</int:service-activator>
<int:gateway request-channel="inputA"/>
</int:chain>

<int:chain id="nested-chain-a" input-channel="inputA">
<int:header-enricher>
<int:header name="name" value="Moe" />
</int:header-enricher>
<int:gateway request-channel="inputB"/>
<int:service-activator>
<bean class="org.foo.SampleService" />
</int:service-activator>
</int:chain>

<int:chain id="nested-chain-b" input-channel="inputB">
<int:header-enricher>
<int:header name="name" value="Jack" />
</int:header-enricher>
<int:service-activator>
<bean class="org.foo.SampleService" />
</int:service-activator>
</int:chain>

在前面的示例中,nested-chain-a 是在 main-chain 处理结束时,由其中配置的 'gateway' 元素调用的。在 nested-chain-a 中,在头部增强之后,会调用 nested-chain-b。然后流程返回到 nested-chain-b 完成执行。最后,流程返回到 main-chain。当在链中定义了 <gateway> 元素的嵌套版本时,它不需要 service-interface 属性。相反,它会接收消息的当前状态,并将其放置在 request-channel 属性定义的通道上。当由该网关启动的下游流程完成时,会返回一个 Message 到网关,并在当前链中继续其旅程。