路由条
从版本 4.1 开始,Spring Integration 提供了 路由条 企业集成模式的实现。它被实现为一个 routingSlip
消息头,用于在端点未指定 outputChannel
时确定 AbstractMessageProducingHandler
实例中的下一个通道。此模式在复杂、动态的情况下非常有用,因为此时配置多个路由器来确定消息流可能会变得困难。当消息到达没有 output-channel
的端点时,将查阅 routingSlip
来确定消息应发送到哪个下一个通道。当路由条用尽时,恢复正常的 replyChannel
处理。
路由条的配置表示为一种 HeaderEnricher
选项——一个包含以分号分隔的路由条,其中包含 path
条目,如下例所示
<util:properties id="properties">
<beans:prop key="myRoutePath1">channel1</beans:prop>
<beans:prop key="myRoutePath2">request.headers[myRoutingSlipChannel]</beans:prop>
</util:properties>
<context:property-placeholder properties-ref="properties"/>
<header-enricher input-channel="input" output-channel="process">
<routing-slip
value="${myRoutePath1}; @routingSlipRoutingPojo.get(request, reply);
routingSlipRoutingStrategy; ${myRoutePath2}; finishChannel"/>
</header-enricher>
上例包含
-
一个
<context:property-placeholder>
配置,用于演示路由条path
中的条目可以指定为可解析的键。 -
使用
<header-enricher>
<routing-slip>
子元素将RoutingSlipHeaderValueMessageProcessor
填充到HeaderEnricher
处理器中。 -
RoutingSlipHeaderValueMessageProcessor
接受已解析路由条path
条目的String
数组,并从processMessage()
返回一个以path
作为key
,以0
作为初始routingSlipIndex
的singletonMap
。
路由条 path
条目可以包含 MessageChannel
的 bean 名称、RoutingSlipRouteStrategy
的 bean 名称和 Spring 表达式 (SpEL)。RoutingSlipHeaderValueMessageProcessor
在首次调用 processMessage
时,会根据 BeanFactory
检查每个路由条 path
条目。它将(在应用程序上下文中不是 bean 名称的)条目转换为 ExpressionEvaluatingRoutingSlipRouteStrategy
实例。RoutingSlipRouteStrategy
条目会被多次调用,直到它们返回 null 或空 String
。
由于路由条参与到 getOutputChannel
过程中,我们有一个请求-应答上下文。RoutingSlipRouteStrategy
的引入是为了确定使用 requestMessage
和 reply
对象的下一个 outputChannel
。此策略的实现应该在应用程序上下文中注册为一个 bean,其 bean 名称在路由条 path
中使用。提供了 ExpressionEvaluatingRoutingSlipRouteStrategy
实现。它接受一个 SpEL 表达式,并将内部的 ExpressionEvaluatingRoutingSlipRouteStrategy.RequestAndReply
对象用作评估上下文的根对象。这样做是为了避免每次调用 ExpressionEvaluatingRoutingSlipRouteStrategy.getNextPath()
时创建 EvaluationContext
的开销。它是一个简单的 Java bean,具有两个属性:Message<?> request
和 Object reply
。通过这种表达式实现,我们可以使用 SpEL 指定路由条 path
条目(例如,@routingSlipRoutingPojo.get(request, reply)
和 request.headers[myRoutingSlipChannel]
),并避免为 RoutingSlipRouteStrategy
定义一个 bean。
requestMessage 参数始终是 Message<?> 。根据上下文,应答对象可能是一个 Message<?> 、一个 AbstractIntegrationMessageBuilder 或任意应用程序域对象(例如,由服务激活器调用的 POJO 方法返回时)。在前两种情况下,使用 SpEL(或 Java 实现)时,通常的 Message 属性(payload 和 headers )可用。对于任意域对象,这些属性不可用。因此,如果结果用于确定下一个路径,在使用路由条结合 POJO 方法时要小心。 |
如果路由条涉及分布式环境,我们建议不要在路由条 path 中使用内联表达式。此建议适用于跨 JVM 应用程序、通过消息代理(例如AMQP 支持或 JMS 支持)使用 request-reply ,或在集成流中使用持久的 MessageStore (消息存储)等分布式环境。框架使用 RoutingSlipHeaderValueMessageProcessor 将它们转换为 ExpressionEvaluatingRoutingSlipRouteStrategy 对象,并在 routingSlip 消息头中使用。由于此类不可 Serializable (因为它依赖于 BeanFactory ,所以不可能),整个 Message 变得不可序列化,并且在任何分布式操作中,都会遇到 NotSerializableException 。为了克服此限制,请注册一个带有所需 SpEL 的 ExpressionEvaluatingRoutingSlipRouteStrategy bean,并在路由条 path 配置中使用其 bean 名称。 |
对于 Java 配置,您可以将 RoutingSlipHeaderValueMessageProcessor
实例添加到 HeaderEnricher
bean 定义中,如下例所示
@Bean
@Transformer(inputChannel = "routingSlipHeaderChannel")
public HeaderEnricher headerEnricher() {
return new HeaderEnricher(Collections.singletonMap(IntegrationMessageHeaderAccessor.ROUTING_SLIP,
new RoutingSlipHeaderValueMessageProcessor("myRoutePath1",
"@routingSlipRoutingPojo.get(request, reply)",
"routingSlipRoutingStrategy",
"request.headers[myRoutingSlipChannel]",
"finishChannel")));
}
当端点生成应答且未定义 outputChannel
时,路由条算法的工作方式如下
-
routingSlipIndex
用于从路由条path
列表中获取值。 -
如果来自
routingSlipIndex
的值是String
,则用于从BeanFactory
获取 bean。 -
如果返回的 bean 是
MessageChannel
的实例,则将其用作下一个outputChannel
,并且在应答消息头中增加routingSlipIndex
(路由条path
条目保持不变)。 -
如果返回的 bean 是
RoutingSlipRouteStrategy
的实例,并且其getNextPath
未返回空String
,则该结果用作下一个outputChannel
的 bean 名称。routingSlipIndex
保持不变。 -
如果
RoutingSlipRouteStrategy.getNextPath
返回空String
或null
,则增加routingSlipIndex
,并递归调用getOutputChannelFromRoutingSlip
以获取下一个路由条path
项。 -
如果下一个路由条
path
条目不是String
,则它必须是RoutingSlipRouteStrategy
的实例。 -
当
routingSlipIndex
超出路由条path
列表的大小限制时,算法将转向标准replyChannel
头的默认行为。