Message Handler Chain

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

The MessageHandlerChain is an implementation of MessageHandler that can be configured as a single message endpoint while actually delegating to a chain of other handlers, such as filters, transformers, splitters, and so on. When several handlers need to be connected in a fixed, linear progression, this can lead to a much simpler configuration. For example, it is fairly common to provide a transformer before other components. Similarly, when you provide a filter before some other component in a chain, you essentially create a selective consumer. In either case, the chain requires only a single input-channel and a single output-channel, eliminating the need to define channels for each individual component.

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

The MessageHandlerChain is mostly designed for an XML configuration. For Java DSL, an IntegrationFlow definition can be treated as a chain component, but it has nothing to do with concepts and principles described in this chapter below. See Java DSL for more information.

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

Spring Integration’s Filter provides a boolean property: throwExceptionOnRejection. When you provide multiple selective consumers on the same point-to-point channel with different acceptance criteria, you should set this value 'true' (the default is false) so that the dispatcher knows that the message was rejected and, as a result, tries to pass the message on to other subscribers. If the exception were not thrown, it would appear to the dispatcher that the message had been passed on successfully even though the filter had dropped the message to prevent further processing. If you do indeed want to “drop” the messages, the filter’s 'discard-channel' might be useful, since it does give you a chance to perform some operation with the dropped message (such as sending it to a JMS queue or writing it to a log).

处理程序链简化了配置,同时在内部保持组件之间相同程度的松散耦合,并且如果在某个时刻需要非线性排列,则修改配置非常简单。

The handler chain simplifies configuration while internally maintaining the same degree of loose coupling between components, and it is trivial to modify the configuration if at some point a non-linear arrangement is required.

在内部,该链被扩展为一个由匿名通道分隔的列出端点的线性设置。回复通道头在链中未被考虑。只有在调用最后一个处理程序之后,结果消息才会转发到回复通道或链的输出通道。由于这个设置,除了最后一个之外的所有处理程序都必须实现 MessageProducer 接口(它提供了 'setOutputChannel()' 方法)。如果设置了 MessageHandlerChain 上的 outputChannel,则最后一个处理程序只需要一个输出通道。

Internally, the chain is expanded into a linear setup of the listed endpoints, separated by anonymous channels. The reply channel header is not taken into account within the chain. Only after the last handler is invoked is the resulting message forwarded to the reply channel or the chain’s output channel. Because of this setup, all handlers except the last must implement the MessageProducer interface (which provides a 'setOutputChannel()' method). If the outputChannel on the MessageHandlerChain is set, the last handler needs only an output channel.

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

As with other endpoints, the output-channel is optional. If there is a reply message at the end of the chain, the output-channel takes precedence. However, if it is not available, the chain handler checks for a reply channel header on the inbound message as a fallback.

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

In most cases, you need not implement MessageHandler yourself. The next section focuses on namespace support for the chain element. Most Spring Integration endpoints, such as service activators and transformers, are suitable for use within a MessageHandlerChain.

Configuring a Chain

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

The <chain> element provides an input-channel attribute. If the last element in the chain is capable of producing reply messages (optional), it also supports an output-channel attribute. The sub-elements are then filters, transformers, splitters, and service-activators. The last element may also be a router or an outbound channel adapter. The following example shows a chain definition:

<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 是一个更简单的选择。

The <header-enricher> element used in the preceding example sets a message header named thing1 with a value of thing2 on the message. A header enricher is a specialization of Transformer that touches only header values. You could obtain the same result by implementing a MessageHandler that did the header modifications and wiring that as a bean, but the header-enricher is a simpler option.

<chain> 可以配置为消息流的最后一个“封闭框”使用者。对于此解决方案,你可以将其放在 <chain> 的末尾,一些 <outbound-channel-adapter>,如下例所示:

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:

<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 不允许在链中使用的组件上指定。轮询子元素也是如此。

Certain attributes, such as order and input-channel are not allowed to be specified on components used within a chain. The same is true for the poller sub-element.

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

For the Spring Integration core components, the XML schema itself enforces some of these constraints. However, for non-core components or your own custom components, these constraints are enforced by the XML namespace parser, not by the XML schema.

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

These XML namespace parser constraints were added with Spring Integration 2.2. If you try to use disallowed attributes and elements, the XML namespace parser throws a BeanDefinitionParsingException.

Using the 'id' Attribute

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

Beginning with Spring Integration 3.0, if a chain element is given an id attribute, the bean name for the element is a combination of the chain’s id and the id of the element itself. Elements without id attributes are not registered as beans, but each one is given a componentName that includes the chain id. Consider the following example:

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

在前一个示例中:

In the preceding example:

  • The <chain> root element has an id of 'somethingChain'. Consequently, the AbstractEndpoint implementation (PollingConsumer or EventDrivenConsumer, depending on the input-channel type) bean takes this value as its bean name.

  • The MessageHandlerChain bean acquires a bean alias ('somethingChain.handler'), which allows direct access to this bean from the BeanFactory.

  • The <service-activator> is not a fully fledged messaging endpoint (it is not a PollingConsumer or EventDrivenConsumer). It is a MessageHandler within the <chain>. In this case, the bean name registered with the BeanFactory is 'somethingChain$child.somethingService.handler'.

  • The componentName of this ServiceActivatingHandler takes the same value but without the '.handler' suffix. It becomes 'somethingChain$child.somethingService'.

  • The last <chain> sub-component, <object-to-json-transformer>, does not have an id attribute. Its componentName is based on its position in the <chain>. In this case, it is 'somethingChain$child#1'. (The final element of the name is the order within the chain, beginning with '#0'). Note, this transformer is not registered as a bean within the application context, so it does not get a beanName. However, its componentName has a value that is useful for logging and other purposes.

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

The id attribute for <chain> elements lets them be eligible for JMX export, and they are trackable in the message history. You can access them from the BeanFactory by using the appropriate bean name, as discussed earlier.

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

It is useful to provide an explicit id attribute on <chain> elements to simplify the identification of sub-components in logs and to provide access to them from the BeanFactory etc.

Calling a Chain from within a Chain

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

Sometimes, you need to make a nested call to another chain from within a chain and then come back and continue execution within the original chain. To accomplish this, you can use a messaging gateway by including a <gateway> element, as the following example shows:

<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 会返回到网关,并在当前链内继续其旅程。

In the preceding example, nested-chain-a is called at the end of main-chain processing by the 'gateway' element configured there. While in nested-chain-a, a call to a nested-chain-b is made after header enrichment. Then the flow comes back to finish execution in nested-chain-b. Finally, the flow returns to main-chain. When the nested version of a <gateway> element is defined in the chain, it does not require the service-interface attribute. Instead, it takes the message in its current state and places it on the channel defined in the request-channel attribute. When the downstream flow initiated by that gateway completes, a Message is returned to the gateway and continues its journey within the current chain.