路由條

從版本 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 作為初始 routingSlipIndexsingletonMap

路由條 path 條目可以包含 MessageChannel 的 bean 名稱、RoutingSlipRouteStrategy 的 bean 名稱和 Spring 表示式 (SpEL)。RoutingSlipHeaderValueMessageProcessor 在首次呼叫 processMessage 時,會根據 BeanFactory 檢查每個路由條 path 條目。它將(在應用程式上下文中不是 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 頭的預設行為。