路由单
从 4.1 版本开始,Spring Integration 提供了 路由单 企业集成模式的实现。它作为 routingSlip
消息头实现,用于确定 AbstractMessageProducingHandler
实例中的下一个通道,当端点的 outputChannel
未指定时。当配置多个路由器来确定消息流变得困难时,这种模式在复杂、动态的情况下非常有用。当消息到达没有 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()
) 一个singletonMap
,其中path
为key
,初始routingSlipIndex
为0
。
路由单 path
条目可以包含 MessageChannel
bean 名称、RoutingSlipRouteStrategy
bean 名称和 Spring 表达式 (SpEL)。RoutingSlipHeaderValueMessageProcessor
在第一次 processMessage
调用时检查每个路由单 path
条目是否与 BeanFactory
匹配。它将条目(不是应用程序上下文中的 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
头的默认行为。