訊息處理器鏈
MessageHandlerChain
是 MessageHandler
的一個實現,它可以配置為單個訊息端點,同時實際委託給其他處理器的鏈,例如過濾器、轉換器、分發器等。當多個處理器需要以固定的線性順序連線時,這可以帶來更簡單的配置。例如,在其他元件之前提供轉換器是相當常見的。類似地,當你在鏈中的其他元件之前提供過濾器時,你實際上建立了一個選擇性消費者(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>
不允許的屬性和元素
鏈中使用的元件不允許指定某些屬性,例如 對於 Spring Integration 核心元件,XML 模式本身強制執行部分約束。然而,對於非核心元件或你自己的自定義元件,這些約束是由 XML 名稱空間解析器強制執行的,而不是由 XML 模式強制執行的。 這些 XML 名稱空間解析器約束是在 Spring Integration 2.2 中新增的。如果你嘗試使用不允許的屬性和元素,XML 名稱空間解析器會丟擲 |
使用 'id' 屬性
從 Spring Integration 3.0 開始,如果 chain 元素被賦予 id
屬性,則該元素的 bean 名稱是鏈的 id
與元素自身 id
的組合。沒有 id
屬性的元素不會註冊為 bean,但每個元素都會獲得一個包含鏈 id
的 componentName
。考慮以下示例
<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
實現(PollingConsumer
或EventDrivenConsumer
,取決於input-channel
型別)的 bean 將此值作為其 bean 名稱。 -
MessageHandlerChain
bean 獲取一個 bean 別名 ('somethingChain.handler'),這允許從 BeanFactory 直接訪問此 bean。 -
<service-activator>
不是一個完全成熟的訊息端點(它不是PollingConsumer
或EventDrivenConsumer
)。它是<chain>
中的一個MessageHandler
。在這種情況下,在 BeanFactory 中註冊的 bean 名稱是 'somethingChain$child.somethingService.handler'。 -
此
ServiceActivatingHandler
的componentName
與上述值相同,但不包含 '.handler' 字尾。它變為 'somethingChain$child.somethingService'。 -
最後一個
<chain>
子元件,<object-to-json-transformer>
,沒有id
屬性。它的componentName
基於其在<chain>
中的位置。在本例中,它是 'somethingChain$child#1'。(名稱的最後一個元素是其在鏈中的順序,從 '#0' 開始)。請注意,此轉換器未在應用程式上下文中註冊為 bean,因此沒有beanName
。但是,它的componentName
有一個值,對於日誌記錄和其他目的非常有用。
在 <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
被返回到閘道器並在當前鏈中繼續其流程。