Routing Slip
从 4.1 版本开始,Spring Integration 提供了 routing slip 企业集成模式的实现。它作为 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
数组,并返回一个singletonMap
(其中path
为key
,0
为初始routingSlipIndex
)。
路由单据 path
条目可以包含 MessageChannel
Bean 名称、RoutingSlipRouteStrategy
Bean 名称和 Spring 表达式 (SpEL)。RoutingSlipHeaderValueMessageProcessor
在第一次 processMessage
调用时根据 BeanFactory
检查每个路由单据 path
条目。它将条目(其不是应用程序上下文中 Bean 名称)转换为 ExpressionEvaluatingRoutingSlipRouteStrategy
实例。RoutingSlipRouteStrategy
条目多次调用,直到它们返回 null
或空 String
为止。
由于路由单参与了 getOutputChannel
流程,因此我们有一个请求回复上下文。已经引入了 RoutingSlipRouteStrategy
来确定下一个 outputChannel
,该 outputChannel
使用 requestMessage
和 reply
对象。此策略的实现应在应用程序上下文中注册为一个 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。
|
如果在分布式环境中涉及路由单据,我们建议不要对路由单据 path
使用行内表达式。此建议适用于分布式环境,例如在消息代理(如 AMQP Support 或 JMS Support)中使用 request-reply
的跨 JVM 应用程序,或在集成流中使用持久 MessageStore
(Message Store)。框架使用 RoutingSlipHeaderValueMessageProcessor
将它们转换为 ExpressionEvaluatingRoutingSlipRouteStrategy
对象,并在 routingSlip
消息头中使用它们。由于此类不是 Serializable
(它不可能是,因为它依赖于 BeanFactory
),因此整个 Message
将变为不可序列化且在任何分布式操作中,我们最终都会得到 NotSerializableException
。要克服此限制,请在 ExpressionEvaluatingRoutingSlipRouteStrategy
中使用期望的 SpEL 注册一个 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
,并针对下一个路由单path
项递归调用getOutputChannelFromRoutingSlip
。 -
如果下一个路由单
path
不是String
,则它一定是RoutingSlipRouteStrategy
的实例。 -
当
routingSlipIndex
超过路由单path
列表的大小时,该算法将转为标准replyChannel
头的默认行为。