訊息處理器鏈

MessageHandlerChainMessageHandler 的一種實作,可以設定為單一訊息端點,同時實際委派給其他處理器的鏈,例如篩選器、轉換器、分割器等等。當數個處理器需要在固定的線性進程中連接時,這可以簡化設定。例如,在其他組件之前提供轉換器是很常見的。同樣地,當您在鏈中的其他組件之前提供篩選器時,您基本上建立了一個選擇性消費者。在任何一種情況下,鏈只需要單一 input-channel 和單一 output-channel,從而無需為每個個別組件定義通道。

MessageHandlerChain 主要為 XML 設定而設計。對於 Java DSL,IntegrationFlow 定義可以視為鏈組件,但它與以下章節中描述的概念和原則無關。有關更多資訊,請參閱 Java DSL
Spring Integration 的 Filter 提供了一個布林屬性:throwExceptionOnRejection。當您在具有不同接受標準的同一個點對點通道上提供多個選擇性消費者時,您應該將此值設定為 'true'(預設值為 false),以便調度器知道訊息已被拒絕,並因此嘗試將訊息傳遞給其他訂閱者。如果沒有拋出例外,則調度器會認為訊息已成功傳遞,即使篩選器已丟棄訊息以防止進一步處理。如果您確實想要「丟棄」訊息,篩選器的 'discard-channel' 可能很有用,因為它確實讓您有機會對丟棄的訊息執行某些操作(例如將其發送到 JMS 佇列或將其寫入日誌)。

處理器鏈簡化了設定,同時在內部保持組件之間相同的鬆散耦合程度,並且如果在某個時候需要非線性排列,則修改設定是微不足道的。

在內部,鏈會展開為列出的端點的線性設定,並由匿名通道分隔。回覆通道標頭在鏈內不被考慮。只有在調用最後一個處理器之後,產生的訊息才會轉發到回覆通道或鏈的輸出通道。由於這種設定,除了最後一個處理器之外的所有處理器都必須實作 MessageProducer 介面(該介面提供 'setOutputChannel()' 方法)。如果在 MessageHandlerChain 上設定了 outputChannel,則最後一個處理器只需要一個輸出通道。

與其他端點一樣,output-channel 是可選的。如果在鏈的末端有回覆訊息,則 output-channel 優先。但是,如果它不可用,則鏈處理器會檢查輸入訊息上的回覆通道標頭作為後備。

在大多數情況下,您不需要自己實作 MessageHandler。下一節重點介紹鏈元素的命名空間支援。大多數 Spring Integration 端點,例如服務啟動器和轉換器,都適用於在 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。標頭豐富器是僅觸及標頭值的 Transformer 的特殊化。您可以透過實作執行標頭修改的 MessageHandler 並將其作為 bean 連線來獲得相同的結果,但標頭豐富器是一個更簡單的選項。

<chain> 可以設定為訊息流的最後一個「封閉盒」消費者。對於此解決方案,您可以將某些 <outbound-channel-adapter> 放在 <chain> 的末端,如下列範例所示

<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-channel,不允許在鏈中使用的組件上指定。輪詢器子元素也是如此。

對於 Spring Integration 核心組件,XML 綱要本身強制執行其中一些限制。但是,對於非核心組件或您自己的自訂組件,這些限制由 XML 命名空間解析器強制執行,而不是由 XML 綱要強制執行。

這些 XML 命名空間解析器限制是在 Spring Integration 2.2 中新增的。如果您嘗試使用不允許的屬性和元素,XML 命名空間解析器會拋出 BeanDefinitionParsingException

使用 'id' 屬性

從 Spring Integration 3.0 開始,如果鏈元素被賦予 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> 根元素具有 'somethingChain' 的 id。因此,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>

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