訊息處理器鏈
MessageHandlerChain 是 MessageHandler 的一個實現,它可以被配置為一個單一的訊息端點,同時實際上委託給一個由其他處理器組成的鏈,例如過濾器、轉換器、拆分器等。當多個處理器需要以固定、線性的方式連線時,這可以大大簡化配置。例如,在其他元件之前提供一個轉換器是很常見的。類似地,當你在鏈中某個其他元件之前提供一個過濾器時,你實際上建立了一個 選擇性消費者。在這兩種情況下,鏈只需要一個 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>
|
禁止的屬性和元素
不允許在鏈中使用的元件上指定某些屬性,例如 對於 Spring Integration 核心元件,XML 模式本身強制執行一些這些約束。但是,對於非核心元件或你自己的自定義元件,這些約束由 XML 名稱空間解析器強制執行,而不是由 XML 模式強制執行。 這些 XML 名稱空間解析器約束是在 Spring Integration 2.2 中新增的。如果你嘗試使用不允許的屬性和元素,XML 名稱空間解析器會丟擲 |
使用 'id' 屬性
從 Spring Integration 3.0 開始,如果鏈元素具有 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 名稱。 -
MessageHandlerChainbean 獲取一個 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>
在前面的示例中,nested-chain-a 在 main-chain 處理結束時由那裡配置的“閘道器”元素呼叫。在 nested-chain-a 中,在訊息頭豐富後呼叫 nested-chain-b。然後流返回到 nested-chain-b 完成執行。最後,流返回到 main-chain。當在鏈中定義 <gateway> 元素的巢狀版本時,它不需要 service-interface 屬性。相反,它接收當前狀態下的訊息並將其放置在 request-channel 屬性中定義的通道上。當由該閘道器啟動的下游流完成後,Message 返回到閘道器並繼續在當前鏈中執行。