路由单

从 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,其中 pathkey,初始 routingSlipIndex0

路由单 path 条目可以包含 MessageChannel bean 名称、RoutingSlipRouteStrategy bean 名称和 Spring 表达式 (SpEL)。RoutingSlipHeaderValueMessageProcessor 在第一次 processMessage 调用时检查每个路由单 path 条目是否与 BeanFactory 匹配。它将条目(不是应用程序上下文中的 bean 名称)转换为 ExpressionEvaluatingRoutingSlipRouteStrategy 实例。RoutingSlipRouteStrategy 条目被多次调用,直到它们返回 null 或空 String

由于路由单参与 getOutputChannel 过程,我们有一个请求-回复上下文。RoutingSlipRouteStrategy 已被引入以确定使用 requestMessagereply 对象的下一个 outputChannel。此策略的实现应注册为应用程序上下文中的 bean,其 bean 名称用于路由单 path 中。提供了 ExpressionEvaluatingRoutingSlipRouteStrategy 实现。它接受一个 SpEL 表达式,并且内部 ExpressionEvaluatingRoutingSlipRouteStrategy.RequestAndReply 对象用作评估上下文的根对象。这是为了避免为每次 ExpressionEvaluatingRoutingSlipRouteStrategy.getNextPath() 调用创建 EvaluationContext 的开销。它是一个简单的 Java bean,具有两个属性:Message<?> requestObject reply。使用此表达式实现,我们可以通过使用 SpEL 指定路由单 path 条目(例如,@routingSlipRoutingPojo.get(request, reply)request.headers[myRoutingSlipChannel]),并避免为 RoutingSlipRouteStrategy 定义 bean。

requestMessage 参数始终为 Message<?>。根据上下文,回复对象可以是 Message<?>AbstractIntegrationMessageBuilder 或任意应用程序域对象(例如,当它由服务激活器调用的 POJO 方法返回时)。在前两种情况下,使用 SpEL(或 Java 实现)时,可以使用通常的 Message 属性(payloadheaders)。对于任意域对象,这些属性不可用。因此,如果结果用于确定下一个路径,请在将路由单与 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 返回空 Stringnull,则 routingSlipIndex 会递增,并且会递归调用 getOutputChannelFromRoutingSlip 以获取下一个路由单 path 项目。

  • 如果下一个路由单 path 条目不是 String,则它必须是 RoutingSlipRouteStrategy 的实例。

  • routingSlipIndex 超过路由单 path 列表的大小时,算法将移动到标准 replyChannel 头的默认行为。