訊息處理器鏈

MessageHandlerChainMessageHandler 的一個實現,它可以配置為單個訊息端點,同時實際委託給其他處理器的鏈,例如過濾器、轉換器、分發器等。當多個處理器需要以固定的線性順序連線時,這可以帶來更簡單的配置。例如,在其他元件之前提供轉換器是相當常見的。類似地,當你在鏈中的其他元件之前提供過濾器時,你實際上建立了一個選擇性消費者(selective consumer)。在任何一種情況下,鏈都只需要一個 input-channel 和一個 output-channel,從而消除了為每個獨立元件定義通道的需要。

MessageHandlerChain 主要設計用於 XML 配置。對於 Java DSL,IntegrationFlow 定義可以被視為鏈元件,但這與本章下面描述的概念和原則無關。有關更多資訊,請參閱Java DSL
Spring Integration 的 Filter 提供了一個布林屬性:throwExceptionOnRejection。當你在同一個點對點通道上提供多個具有不同接受條件的 selective consumer 時,你應該將此值設定為 'true'(預設值為 false),以便排程器知道訊息已被拒絕,因此嘗試將訊息傳遞給其他訂閱者。如果未丟擲異常,則排程器會認為訊息已成功傳遞,即使過濾器已丟棄訊息以防止進一步處理。如果確實想“丟棄”訊息,過濾器的 'discard-channel' 可能很有用,因為它確實為你提供了對丟棄的訊息執行某些操作的機會(例如將其傳送到 JMS 佇列或寫入日誌)。

處理器鏈簡化了配置,同時在內部保持了元件之間相同程度的鬆散耦合,並且如果在某個時刻需要非線性排列,修改配置也非常簡單。

在內部,鏈被擴充套件為所列端點的線性設定,由匿名通道分隔。鏈內不考慮回覆通道(reply channel)頭。只有在最後一個處理器被呼叫後,結果訊息才會被轉發到回覆通道(reply channel)或鏈的輸出通道(output channel)。由於這種設定,除最後一個處理器外,所有處理器都必須實現 MessageProducer 介面(該介面提供了 'setOutputChannel()' 方法)。如果在 MessageHandlerChain 上設定了 outputChannel,則最後一個處理器只需要一個 output channel。

與其他端點一樣,output-channel 是可選的。如果在鏈的末尾有回覆訊息,output-channel 具有優先權。但是,如果它不可用,鏈處理器會檢查入站訊息上的回覆通道頭(reply channel header)作為備用。

在大多數情況下,你無需自己實現 MessageHandler。下一節重點介紹 chain 元素的名稱空間支援。大多數 Spring Integration 端點,例如 service activators 和 transformers,都適合在 MessageHandlerChain 中使用。

配置鏈

<chain> 元素提供了 input-channel 屬性。如果鏈中的最後一個元素能夠產生回覆訊息(可選),它也支援 output-channel 屬性。其子元素可以是過濾器、轉換器、分發器和服務啟用器。最後一個元素也可以是路由器或出站通道介面卡。以下示例展示了鏈的定義

<int:chain input-channel="input" output-channel="output">
    <int:filter ref="someSelector" throw-exception-on-rejection="true"/>
    <int:header-enricher>
        <int:header name="thing1" value="thing2"/>
    </int:header-enricher>
    <int:service-activator ref="someService" method="someMethod"/>
</int:chain>

前面示例中使用的 <header-enricher> 元素在訊息上設定了一個名為 thing1、值為 thing2 的訊息頭。header enricher 是 Transformer 的一種特殊形式,它僅修改 header 值。你可以透過實現一個 MessageHandler 來完成這些 header 修改並將其配置為一個 bean,從而獲得相同的結果,但 header-enricher 是一種更簡單的選擇。

可以將 <chain> 配置為訊息流的最後一個“封閉式”消費者。為此,可以在 <chain> 的末尾放置一些 <outbound-channel-adapter>,如下例所示

<int:chain input-channel="input">
    <int-xml:marshalling-transformer marshaller="marshaller" result-type="StringResult" />
    <int:service-activator ref="someService" method="someMethod"/>
    <int:header-enricher>
        <int:header name="thing1" value="thing2"/>
    </int:header-enricher>
    <int:logging-channel-adapter level="INFO" log-full-message="true"/>
</int:chain>
不允許的屬性和元素

鏈中使用的元件不允許指定某些屬性,例如 orderinput-channelpoller 子元素也是如此。

對於 Spring Integration 核心元件,XML 模式本身強制執行部分約束。然而,對於非核心元件或你自己的自定義元件,這些約束是由 XML 名稱空間解析器強制執行的,而不是由 XML 模式強制執行的。

這些 XML 名稱空間解析器約束是在 Spring Integration 2.2 中新增的。如果你嘗試使用不允許的屬性和元素,XML 名稱空間解析器會丟擲 BeanDefinitionParsingException

使用 'id' 屬性

從 Spring Integration 3.0 開始,如果 chain 元素被賦予 id 屬性,則該元素的 bean 名稱是鏈的 id 與元素自身 id 的組合。沒有 id 屬性的元素不會註冊為 bean,但每個元素都會獲得一個包含鏈 idcomponentName。考慮以下示例

<int:chain id="somethingChain" input-channel="input">
    <int:service-activator id="somethingService" ref="someService" method="someMethod"/>
    <int:object-to-json-transformer/>
</int:chain>

在前述示例中

  • <chain> 根元素的 id 為 'somethingChain'。因此,AbstractEndpoint 實現(PollingConsumerEventDrivenConsumer,取決於 input-channel 型別)的 bean 將此值作為其 bean 名稱。

  • MessageHandlerChain bean 獲取一個 bean 別名 ('somethingChain.handler'),這允許從 BeanFactory 直接訪問此 bean。

  • <service-activator> 不是一個完全成熟的訊息端點(它不是 PollingConsumerEventDrivenConsumer)。它是 <chain> 中的一個 MessageHandler。在這種情況下,在 BeanFactory 中註冊的 bean 名稱是 'somethingChain$child.somethingService.handler'。

  • ServiceActivatingHandlercomponentName 與上述值相同,但不包含 '.handler' 字尾。它變為 'somethingChain$child.somethingService'。

  • 最後一個 <chain> 子元件,<object-to-json-transformer>,沒有 id 屬性。它的 componentName 基於其在 <chain> 中的位置。在本例中,它是 'somethingChain$child#1'。(名稱的最後一個元素是其在鏈中的順序,從 '#0' 開始)。請注意,此轉換器未在應用程式上下文中註冊為 bean,因此沒有 beanName。但是,它的 componentName 有一個值,對於日誌記錄和其他目的非常有用。

<chain> 元素的 id 屬性使其有資格進行JMX 匯出,並且可以在訊息歷史中追蹤它們。你可以透過使用適當的 bean 名稱從 BeanFactory 訪問它們,如前所述。

<chain> 元素上提供顯式的 id 屬性有助於簡化日誌中子元件的識別,並允許從 BeanFactory 等地方訪問它們。

從鏈內部呼叫鏈

有時,你需要從一個鏈的內部巢狀呼叫另一個鏈,然後返回並在原始鏈中繼續執行。為此,你可以使用訊息閘道器,透過包含一個 <gateway> 元素來實現,如下例所示

<int:chain id="main-chain" input-channel="in" output-channel="out">
    <int:header-enricher>
      <int:header name="name" value="Many" />
    </int:header-enricher>
    <int:service-activator>
      <bean class="org.foo.SampleService" />
    </int:service-activator>
    <int:gateway request-channel="inputA"/>
</int:chain>

<int:chain id="nested-chain-a" input-channel="inputA">
    <int:header-enricher>
        <int:header name="name" value="Moe" />
    </int:header-enricher>
    <int:gateway request-channel="inputB"/>
    <int:service-activator>
        <bean class="org.foo.SampleService" />
    </int:service-activator>
</int:chain>

<int:chain id="nested-chain-b" input-channel="inputB">
    <int:header-enricher>
        <int:header name="name" value="Jack" />
    </int:header-enricher>
    <int:service-activator>
        <bean class="org.foo.SampleService" />
    </int:service-activator>
</int:chain>

在前面的示例中,main-chain 處理結束時由配置在那裡的 'gateway' 元素呼叫 nested-chain-a。在 nested-chain-a 中,進行 header enrichment 後會呼叫 nested-chain-b。然後流程返回並在 nested-chain-b 中完成執行。最後,流程返回到 main-chain。當在鏈中定義巢狀版本的 <gateway> 元素時,它不需要 service-interface 屬性。相反,它獲取訊息的當前狀態並將其放置在 request-channel 屬性定義的通道上。當由該閘道器啟動的下游流完成時,一個 Message 被返回到閘道器並在當前鏈中繼續其流程。