如需使用最新稳定版本,请使用 Spring Integration 7.0.4spring-doc.cadn.net.cn

消息处理器链

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

MessageHandlerChain 主要用于 XML 配置。 对于 Java DSL,IntegrationFlow 定义可被视为链式组件,但它与本章下文描述的概念和原则无关。 有关更多信息,请参阅 Java DSL
Spring Integration 的 Filter 提供了一个布尔属性:throwExceptionOnRejection。 当您在同一个点对点通道上提供多个具有不同接受条件的选择性消费者时,您应该将此值设置为 'true'(默认值为 false),以便调度器知道消息已被拒绝,从而尝试将消息传递给其他订阅者。 如果未抛出异常,对于调度器而言,消息似乎已成功传递,尽管过滤器实际上已丢弃该消息以阻止进一步处理。 如果您确实希望“丢弃”这些消息,过滤器的'discard-channel'可能很有用,因为它允许您对丢弃的消息执行某些操作(例如将其发送到 JMS 队列或写入日志)。

处理器链简化了配置,同时在内部保持了组件之间相同的低耦合程度,并且如果将来需要非线性排列,修改配置也轻而易举。spring-doc.cadn.net.cn

在内部,链被展开为所列端点的线性设置,由匿名通道分隔。 回复通道头在链中不予考虑。 只有在调用最后一个处理器后,生成的消息才会被转发到回复通道或链的输出通道。 由于这种设置,除最后一个之外的所有处理器必须实现MessageProducer接口(该接口提供'setOutputChannel()'方法)。 如果outputChannel上的MessageHandlerChain已设置,则最后一个处理器只需要一个输出通道。spring-doc.cadn.net.cn

与其他端点一样,output-channel 是可选的。 如果链的末尾存在回复消息,则 output-channel 将优先使用。 但是,如果不可用,链处理器将检查入站消息上的 reply channel 头部作为回退方案。

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

配置链

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

<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 来获得相同的结果,但使用头部丰富器是一个更简单的选择。spring-doc.cadn.net.cn

The <chain> can be configured as the last “closed-box” consumer of the message flow. For this solution, you can to put it at the end of the <chain> some <outbound-channel-adapter>, as the following example shows:spring-doc.cadn.net.cn

<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>
不允许使用的属性和元素

某些属性,例如 orderinput-channel,不允许在链中使用的组件上指定。 对于 poller 子元素也是如此。spring-doc.cadn.net.cn

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

这些 XML 命名空间解析器约束是在 Spring Integration 2.2 版本中新增的。 如果您尝试使用不允许的属性和元素,XML 命名空间解析器将抛出 BeanDefinitionParsingExceptionspring-doc.cadn.net.cn

使用 'id' 属性

从 Spring Integration 3.0 开始,如果链元素被赋予 id 属性,则该元素的 bean 名称是链的 id 与元素自身的 id 的组合。 没有 id 属性的元素不会被注册为 bean,但每个元素都会被分配一个包含链 idcomponentName。 请考虑以下示例:spring-doc.cadn.net.cn

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

在前面的示例中:spring-doc.cadn.net.cn

  • <chain>根元素具有一个值为'somethingChain'的id。 因此,AbstractEndpoint实现(根据input-channel类型,为PollingConsumerEventDrivenConsumer)的 Bean 将此值作为其 Bean 名称。spring-doc.cadn.net.cn

  • MessageHandlerChain bean 获取了一个 bean 别名('somethingChain.handler'),这使得可以从 BeanFactory 直接访问该 bean。spring-doc.cadn.net.cn

  • <service-activator> 不是一个功能完整的消息端点(它不是 PollingConsumerEventDrivenConsumer)。 它是 <chain> 中的一个 MessageHandler。 在这种情况下,向 BeanFactory 注册的 Bean 名称为 'somethingChain$child.somethingService.handler'。spring-doc.cadn.net.cn

  • componentNameServiceActivatingHandler 采用相同的值,但不带 '.handler' 后缀。 它变为 'somethingChain$child.somethingService'。spring-doc.cadn.net.cn

  • 最后一个 <chain> 子组件 <object-to-json-transformer> 没有 id 属性。 它的 componentName 基于其在 <chain> 中的位置。 在此情况下,它是 'somethingChain$child#1'。 (名称的最终元素是链内的顺序,从 '#0' 开始)。 注意,此转换器未在应用程序上下文中注册为 Bean,因此它不会获得 beanName。 然而,它的 componentName 具有对日志记录和其他用途有价值的值。spring-doc.cadn.net.cn

id 属性适用于 <chain> 元素,使其有资格进行 JMX 导出,并且可以在 消息历史 中进行跟踪。 您可以使用适当的 Bean 名称从 BeanFactory 访问它们,如前所述。spring-doc.cadn.net.cn

为简化日志中子组件的识别并允许从BeanFactory等位置访问它们,建议在<chain>元素上显式提供id属性。

在链中调用另一个链

有时,您需要从某个链内部嵌套调用另一个链,然后返回并继续在原始链中执行。 为此,您可以通过包含 <gateway> 元素来使用消息网关,如下例所示:spring-doc.cadn.net.cn

<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 处理结束时调用。 而在 nested-chain-a 中,在标头丰富之后会调用一个 nested-chain-b。 随后,流程返回到 nested-chain-b 以完成执行。 最后,流程返回到 main-chain。 当链中定义了 <gateway> 元素的嵌套版本时,它不需要 service-interface 属性。 相反,它会获取当前状态下的消息,并将其放置在 request-channel 属性中定义的通道上。 当该网关触发的下游流完成后,会将一个 Message 返回给网关,并继续在当前链中流转。spring-doc.cadn.net.cn