消息处理器链
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 是可选的。如果链末尾有回复消息,则 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> 可以配置为消息流的最后一个“封闭式”消费者。对于此解决方案,你可以将一些 <outbound-channel-adapter> 放在 <chain> 的末尾,如下例所示
<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>
|
禁止的属性和元素
不允许在链中使用的组件上指定某些属性,例如 对于 Spring Integration 核心组件,XML 模式本身强制执行一些这些约束。但是,对于非核心组件或你自己的自定义组件,这些约束由 XML 命名空间解析器强制执行,而不是由 XML 模式强制执行。 这些 XML 命名空间解析器约束是在 Spring Integration 2.2 中添加的。如果你尝试使用不允许的属性和元素,XML 命名空间解析器会抛出 |
使用 '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 名称。 -
MessageHandlerChainbean 获取一个 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>
在前面的示例中,nested-chain-a 在 main-chain 处理结束时由那里配置的“网关”元素调用。在 nested-chain-a 中,在消息头丰富后调用 nested-chain-b。然后流返回到 nested-chain-b 完成执行。最后,流返回到 main-chain。当在链中定义 <gateway> 元素的嵌套版本时,它不需要 service-interface 属性。相反,它接收当前状态下的消息并将其放置在 request-channel 属性中定义的通道上。当由该网关启动的下游流完成后,Message 返回到网关并继续在当前链中运行。