消息处理程序链
MessageHandlerChain
是 MessageHandler
的一个实现,可以配置为单个消息端点,但实际上会委托给其他处理器(如过滤器、转换器、拆分器等)组成的链。当多个处理器需要按固定、线性顺序连接时,这可以简化配置。例如,在其他组件之前提供一个转换器是相当常见的。同样地,当您在链中的某些其他组件之前提供一个过滤器时,实际上创建了一个选择性消费者。在这两种情况下,链只需要一个 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
的一种特化,只修改头部值。你也可以通过实现一个 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>
不允许的属性和元素
某些属性,例如 order
和 input-channel
,不允许在链中使用的组件上指定。poller 子元素也是如此。
对于 Spring Integration 核心组件,XML 模式本身会强制执行其中一些约束。但是,对于非核心组件或您自己的自定义组件,这些约束是由 XML 命名空间解析器强制执行的,而不是由 XML 模式强制执行的。
这些 XML 命名空间解析器约束是在 Spring Integration 2.2 中添加的。如果您尝试使用不允许的属性和元素,XML 命名空间解析器将抛出一个 BeanDefinitionParsingException
。
使用 'id' 属性
从 Spring Integration 3.0 开始,如果链中的元素被赋予了 id
属性,则该元素的 bean 名称是链的 id
和元素本身的 id
的组合。没有 id
属性的元素不会注册为 bean,但每个元素都会被赋予一个包含链 id
的 componentName
。考虑以下示例:
<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
实现 (PollingConsumer
或EventDrivenConsumer
,取决于input-channel
类型) 的 bean 将此值作为其 bean 名称。 -
MessageHandlerChain
bean 获得一个 bean 别名 ('somethingChain.handler'),这允许从BeanFactory
直接访问此 bean。 -
<service-activator>
不是一个完全的消息端点(它不是PollingConsumer
或EventDrivenConsumer
)。它是<chain>
内的一个MessageHandler
。在这种情况下,注册到BeanFactory
的 bean 名称为 'somethingChain$child.somethingService.handler'。 -
此
ServiceActivatingHandler
的componentName
取相同的值但不带 '.handler' 后缀。它变为 'somethingChain$child.somethingService'。 -
最后一个
<chain>
子组件<object-to-json-transformer>
没有id
属性。它的componentName
基于它在<chain>
中的位置。在这种情况下,它是 'somethingChain$child#1'。(名称的最后一个元素是链中的顺序,从 '#0' 开始)。注意,此转换器不会作为 bean 注册到应用程序上下文中,因此它没有beanName
。但是,它的componentName
有一个对日志记录和其他用途有用的值。
在 <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>
在前面的例子中,在 main-chain
处理的结尾处,通过那里配置的 'gateway' 元素调用了 nested-chain-a
。在 nested-chain-a
中,在完成 header 丰富后,会调用 nested-chain-b
。然后流程回到 nested-chain-b
完成执行。最后,流程返回到 main-chain
。当在链中定义 <gateway>
元素的嵌套版本时,它不需要 service-interface
属性。相反,它获取当前状态的消息,并将其放置在 request-channel
属性中定义的通道上。当由该网关启动的下游流程完成后,一个 Message
被返回到网关,并在其当前链内继续其旅程。