消息处理器链

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 是可选的。如果链的末尾有回复消息,则输出通道优先。但是,如果它不可用,则链处理程序会检查传入消息上的回复通道标头作为后备。

在大多数情况下,您无需自己实现 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 的一个专门化,它只处理标头值。您可以通过实现一个执行标头修改并将其作为 bean 进行连接的 MessageHandler 来获得相同的结果,但标头增强器是一个更简单的选项。

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

某些属性(例如 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 实现(PollingConsumerEventDrivenConsumer,具体取决于 input-channel 类型)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 通过在其中配置的“gateway”元素在 main-chain 处理结束时被调用。在 nested-chain-a 中,在标头增强后会调用 nested-chain-b。然后流程返回以完成 nested-chain-b 中的执行。最后,流程返回到 main-chain。当 <gateway> 元素的嵌套版本在链中定义时,它不需要 service-interface 属性。相反,它获取其当前状态下的消息并将其放置在 request-channel 属性中定义的通道上。当该网关启动的下游流程完成时,会将 Message 返回到网关并继续其在当前链中的旅程。