Message Handler Chain

MessageHandlerChainMessageHandler 的一种实现,可以配置为一个单消息端点,同时实际上委托给其他处理程序的链接(例如过滤器、变压器、拆分器等)。当需要以固定的线性级数连接多个处理程序时,这会大大简化配置。例如,在其他组件之前提供一个变压器是相当常见的。同样,当您在链接中的某个其他组件之前提供一个过滤器时,您实际上创建了一个 selective consumer。在任何一种情况下,该链接只需要一个 input-channel 和一个 output-channel,从而无需为每个组件定义通道。

MessageHandlerChain 主要设计用于 XML 配置。对于 Java DSL,IntegrationFlow 定义可以视为链组件,但与本章中描述的概念和原则无关。请参阅 Java DSL 以获取更多信息。

Spring Integration 的 Filter 提供了一个布尔属性:throwExceptionOnRejection。当您在具有不同接受标准的同一点对点通道上提供多个选择性使用者时,应将此值设置为 “true”(默认值为 false),以便调度器知道该邮件已被拒绝,并且因此尝试向其他订阅者传递该邮件。如果未抛出异常,对于调度器而言,即使筛选器已删除该邮件以防止进一步处理,也会显示该邮件已成功传递。如果您确实希望 “drop” 邮件,则筛选器的 “discard-channel” 可能会很有用,因为它确实为您提供了对已删除邮件执行一些操作的机会(例如将邮件发送到 JMS 队列或将其写入日志)。

处理程序链简化了配置,同时在内部保持组件之间相同程度的松散耦合,并且如果在某个时刻需要非线性排列,则修改配置非常简单。 在内部,该链被扩展为一个由匿名通道分隔的列出端点的线性设置。回复通道头在链中未被考虑。只有在调用最后一个处理程序之后,结果消息才会转发到回复通道或链的输出通道。由于这个设置,除了最后一个之外的所有处理程序都必须实现 MessageProducer 接口(它提供了 'setOutputChannel()' 方法)。如果设置了 MessageHandlerChain 上的 outputChannel,则最后一个处理程序只需要一个输出通道。

与其他端点一样,output-channel 是可选的。如果链的末尾有回复消息,则输出频道具有优先级。但是,如果不可用,则链处理程序会检查入站消息上的回复频道头作为后备。

在大多数情况下,你不必要自己实现 MessageHandler。下一节重点关注链元素的命名空间支持。大多数 Spring Integration 端点,例如服务激活器和转换器,都适合在 MessageHandlerChain 中使用。

Configuring a Chain

<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 的一个特化,它只处理头值。你可以通过实现一个执行 header 修改和将其作为 bean 连接在一起的 MessageHandler 来获得相同的结果,但 header enrichner 是一个更简单的选择。

<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>
Example 1. Disallowed Attributes and Elements

某些属性,例如 orderinput-channel 不允许在链中使用的组件上指定。轮询子元素也是如此。 对于 Spring Integration 核心组件,XML 模式本身强制执行一些这些约束。但是,对于非核心组件或你自己的自定义组件,这些约束由 XML 命名空间解析器强制执行,而不是由 XML 模式强制执行。 这些 XML 命名空间解析器约束是在 Spring Integration 2.2 中添加的。如果你尝试使用不允许的属性和元素,XML 命名空间解析器将抛出 BeanDefinitionParsingException

Using the 'id' Attribute

从 Spring Integration 3.0 开始,如果给链元素一个 id 属性,则该元素的 bean 名称是链的 id 和元素本身的 id 的组合。没有 id 属性的元素不会注册为 bean,但每个元素都会得到一个 componentName,其中包含链 id。考虑以下示例:

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

在前一个示例中:

  • &lt;chain&gt; 根元素的 id 为 'somethingChain'。因此,AbstractEndpoint 实现(取决于 input-channel 类型为 PollingConsumerEventDrivenConsumer)bean 将此值作为其 bean 名称。

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

  • &lt;service-activator&gt; 不是成熟的消息传递端点(它不是 PollingConsumerEventDrivenConsumer)。它是 &lt;chain&gt; 中的 MessageHandler。在这种情况下,在 BeanFactory 中注册的 bean 名称是 'somethingChain$child.somethingService.handler'。

  • ServiceActivatingHandlercomponentName 取相同值,但没有 '.handler' 后缀。它变成 'somethingChain$child.somethingService'。

  • 最后一个 &lt;chain&gt; 子组件 &lt;object-to-json-transformer&gt; 没有 id 属性。它的 componentName 基于其在 &lt;chain&gt; 中的位置。在这种情况下,它是 'somethingChain$child#1'。(名称的最后一部分是链中的顺序,从 '#0' 开始)。请注意,此转换器未在应用程序上下文中注册为 bean,因此它不会获得 beanName。然而,它的 componentName 有一个值,这对记录和其他用途很有用。

id 属性对 <chain> 元素允许其有资格获得 JMX export,并且可以在 message history 中进行跟踪。您可以使用之前讨论过的相应 bean 名称从 BeanFactory 访问它们。

<chain> 元素提供一个明确的 id 属性可以简化日志中子组件的识别,并提供从 BeanFactory 等访问它们的能力。

Calling a Chain from within a Chain

有时,你需要从链内嵌套调用另一个链,然后返回并在原始链内继续执行。为了实现这一点,你可以使用消息网关,方法是包含一个 <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>

在前一个示例中,通过那里配置的“网关”元素,在 main-chain 处理的末尾调用了 nested-chain-a。在 nested-chain-a 中,在执行 header 丰富之后调用了 nested-chain-b。然后流程返回到 nested-chain-b 中完成执行。最后,流程返回到 main-chain。当在链中定义 <gateway> 元素的嵌套版本时,它不需要 service-interface 属性。相反,它以其当前状态获取消息,并将其放置在 request-channel 属性中定义的通道上。当由该网关发起的下游流程完成时,Message 会返回到网关,并在当前链内继续其旅程。