Spring Integration 示例
從 Spring Integration 2.0 開始,Spring Integration 發行版不再包含示例。相反,我們已轉向一種更簡單的協作模型,這應該會促進更好的社群參與,並且理想情況下,會有更多的貢獻。示例現在擁有一個專用的 GitHub 倉庫。示例開發也有其自己的生命週期,它不依賴於框架釋出的生命週期,儘管出於相容性原因,該倉庫仍會用每個主要版本進行標記。
對社群的最大好處是,我們現在可以新增更多示例並立即提供給您,而無需等待下一個版本。擁有一個不與實際框架繫結的專用 GitHub 倉庫也是一個巨大的好處。您現在有一個專門的地方可以提出示例建議以及報告現有示例的問題。您也可以透過拉取請求向我們提交示例。如果我們認為您的示例有價值,我們非常樂意將其新增到“samples”倉庫中,並適當註明您為作者。
從何處獲取示例
Spring Integration Samples 專案託管在 GitHub 上。要檢出或克隆示例,您的系統上必須安裝 Git 客戶端。有多種基於 GUI 的產品可用於許多平臺,例如用於 Eclipse IDE 的 EGit。簡單的 Google 搜尋可以幫助您找到它們。您也可以使用 Git 的命令列介面。
| 如果您需要有關如何安裝或使用 Git 的更多資訊,請訪問:https://git-scm.tw/。 |
要使用 Git 命令列工具克隆 (checkout) Spring Integration 示例倉庫,請執行以下命令
$ git clone https://github.com/spring-projects/spring-integration-samples.git
前面的命令會將整個 samples 倉庫克隆到您執行該 git 命令的工作目錄中名為 spring-integration-samples 的目錄中。由於 samples 倉庫是一個即時倉庫,您可能希望定期執行拉取(更新)以獲取新示例和現有示例的更新。為此,請執行以下 git pull 命令
$ git pull
提交示例或示例請求
您可以提交新示例和示例請求。我們非常感謝為改進示例所做的任何努力,包括分享好主意。
我如何貢獻自己的示例?
GitHub 用於社交程式設計:如果您想將自己的程式碼示例提交到 Spring Integration Samples 專案,我們鼓勵透過本倉庫的 forks 中的 拉取請求 進行貢獻。如果您想以這種方式貢獻程式碼,請儘可能引用一個 GitHub issue,其中提供了有關您的示例的一些詳細資訊。
程式碼貢獻流程
有關實際的程式碼貢獻流程,請閱讀 Spring Integration 的貢獻者指南。它們也適用於示例專案。您可以在 github.com/spring-projects/spring-integration/blob/main/CONTRIBUTING.adoc 中找到它們。
此過程確保每個提交都經過同行評審。事實上,核心提交者遵循完全相同的規則。我們衷心期待您的 Spring Integration 示例!
示例請求
如前文所述,Spring Integration 示例專案使用 GitHub issue 作為錯誤跟蹤系統。要提交新的示例請求,請訪問 github.com/spring-projects/spring-integration-samples/issues。
示例結構
從 Spring Integration 2.0 開始,示例的結構發生了變化。隨著更多示例的計劃,我們意識到並非所有示例都具有相同的目標。它們都共享一個共同目標,即向您展示如何應用和使用 Spring Integration 框架。然而,它們的不同之處在於,一些示例側重於技術用例,而另一些則側重於業務用例。此外,一些示例旨在展示可用於解決某些場景(技術和業務)的各種技術。新的示例分類使我們能夠根據每個示例解決的問題更好地組織它們,同時為您提供一種更簡單的方法來找到滿足您需求的正確示例。
目前有四類。在 samples 倉庫中,每個類別都有自己的目錄,該目錄以類別名稱命名。
- 基本 (
samples/basic) -
這是入門的好地方。這裡的示例以技術為導向,並展示了配置和程式碼的最低限度。這些示例應透過向您介紹 Spring Integration 以及企業整合模式 (EIP) 的基本概念、API 和配置,幫助您快速入門。例如,如果您正在尋找如何實現服務啟用器並將其連線到訊息通道、如何使用訊息閘道器作為訊息交換的門面,或者如何開始使用 MAIL、TCP/UDP 或其他模組的答案,這是找到好示例的正確地方。底線是
samples/basic是一個很好的入門之地。 - 中級 (
samples/intermediate) -
此類別針對已熟悉 Spring Integration 框架(不僅限於入門)但需要更多指導以解決切換到訊息架構後可能遇到的更高階技術問題的開發人員。例如,如果您正在尋找有關如何在各種訊息交換場景中處理錯誤、如何為某些訊息永遠不會到達聚合的情況正確配置聚合器,或任何超出特定元件的基本實現和配置並揭示“還有什麼”型別問題的答案,這是找到此類示例的正確地方。
- 高階 (
samples/advanced) -
此類別面向非常熟悉 Spring Integration 框架但希望透過使用 Spring Integration 的公共 API 來擴充套件它以解決特定自定義需求的開發人員。例如,如果您正在尋找示例來展示如何實現自定義通道或消費者(事件驅動或輪詢驅動),或者您正在嘗試找出在 Spring Integration Bean 解析器層次結構之上實現自定義 Bean 解析器的最合適方法(也許在為自定義元件實現自己的名稱空間和模式時),這是正確的地方。在這裡您還可以找到有助於介面卡開發的示例。Spring Integration 附帶了大量的介面卡庫,可讓您將遠端系統與 Spring Integration 訊息傳遞框架連線起來。但是,您可能需要與核心框架不提供介面卡的系統整合。如果是這樣,您可能決定實現自己的介面卡(請考慮貢獻它)。此類別將包含向您展示如何操作的示例。
- 應用程式 (
samples/applications) -
此類別面向對訊息驅動架構和 EIP 有良好理解,並且對 Spring 和 Spring Integration 有高於平均水平理解的開發人員和架構師,他們正在尋找解決特定業務問題的示例。換句話說,此類別中示例的重點是業務用例以及如何使用訊息驅動架構和 Spring Integration 來解決這些問題。例如,如果您想了解如何使用 Spring Integration 實現和自動化貸款經紀人或旅行社流程,這是找到這些型別示例的正確地方。
| Spring Integration 是一個社群驅動的框架。因此,社群參與很重要。這包括示例。如果您找不到您想要的東西,請告訴我們! |
示例
目前,Spring Integration 附帶了相當多的示例,而且您只會期待更多。為了幫助您更好地瀏覽它們,每個示例都附帶自己的 readme.txt 檔案,其中涵蓋了有關示例的幾個詳細資訊(例如,它解決了哪些 EIP 模式,它試圖解決什麼問題,如何執行示例以及其他詳細資訊)。但是,某些示例需要更詳細的解釋,有時還需要圖形解釋。在本節中,您可以找到我們認為需要特別關注的示例的詳細資訊。
貸款經紀人
本節涵蓋 Spring Integration 示例中包含的貸款經紀人示例應用程式。此示例的靈感來自 Gregor Hohpe 和 Bobby Woolf 的著作 企業整合模式 中精選的示例之一。
下圖顯示了整個過程
EIP 架構的核心是非常簡單但功能強大的管道、過濾器概念,當然還有:訊息。端點(過濾器)透過通道(管道)相互連線。生產端點將訊息傳送到通道,消費端點檢索訊息。這種架構旨在定義各種機制,描述資訊如何在端點之間交換,而無需瞭解這些端點是什麼或它們正在交換什麼資訊。因此,它提供了一個非常鬆散耦合和靈活的協作模型,同時還將整合關注點與業務關注點分離。EIP 透過進一步定義擴充套件了此架構
-
管道的型別(點對點通道、釋出-訂閱通道、通道介面卡等)
-
核心過濾器以及過濾器如何與管道協作的模式(訊息路由器、拆分器和聚合器、各種訊息轉換模式等)
EIP 書的第 9 章很好地描述了此用例的詳細資訊和變體,但這裡是簡要總結:在尋找最佳貸款報價時,消費者會訂閱貸款經紀人的服務,該服務處理以下細節:
-
消費者預篩選(例如,獲取和審查消費者的信用記錄)
-
確定最合適的銀行(例如,根據消費者的信用記錄或分數)
-
向每個選定的銀行傳送貸款報價請求
-
收集每個銀行的回覆
-
根據消費者的要求過濾回覆並確定最佳報價。
-
將貸款報價傳回給消費者。
獲得貸款報價的實際過程通常要複雜一些。然而,由於我們的目標是展示企業整合模式如何在 Spring Integration 中實現,因此用例已被簡化,僅關注流程的整合方面。這並非旨在為您提供消費者財務建議。
透過聘請貸款經紀人,消費者與貸款經紀人運營的細節隔離開來,並且每個貸款經紀人的運營可能會為了保持競爭優勢而相互推遲,因此我們組裝和實現的任何東西都必須是靈活的,以便可以快速輕鬆地引入任何更改。
| 貸款經紀人示例實際上並未與任何“假想”的銀行或信用局進行通訊。這些服務都被模擬了。 |
我們在這裡的目標是整體組裝、編排和測試流程的整合方面。只有這樣,我們才能開始考慮將此類流程連線到真實服務。屆時,無論特定貸款經紀人與多少家銀行打交道,或用於與這些銀行通訊的通訊媒體(或協議)型別(JMS、WS、TCP 等),組裝的流程及其配置都不會改變。
設計
當您分析前面列出的六個要求時,您會發現它們都是整合關注點。例如,在消費者預篩選步驟中,我們需要收集有關消費者及其需求的額外資訊,並用額外的元資訊豐富貸款請求。然後我們必須篩選此類資訊以選擇最合適的銀行列表等等。豐富、篩選和選擇都是整合關注點,EIP 以模式的形式定義瞭解決方案。Spring Integration 提供了這些模式的實現。
下圖顯示了訊息閘道器的表示
訊息閘道器模式提供了一種簡單的機制來訪問訊息系統,包括我們的貸款經紀人。在 Spring Integration 中,您可以將閘道器定義為普通的 Java 介面(無需提供實現),使用 XML <gateway> 元素或 Java 中的註解進行配置,並像使用任何其他 Spring bean 一樣使用它。Spring Integration 透過生成訊息(有效負載對映到方法的輸入引數)並將其傳送到指定通道來負責將方法呼叫委派和對映到訊息基礎設施。以下示例顯示瞭如何使用 XML 定義此類閘道器
<int:gateway id="loanBrokerGateway"
default-request-channel="loanBrokerPreProcessingChannel"
service-interface="org.springframework.integration.samples.loanbroker.LoanBrokerGateway">
<int:method name="getBestLoanQuote">
<int:header name="RESPONSE_TYPE" value="BEST"/>
</int:method>
</int:gateway>
我們當前的閘道器提供了兩個可以呼叫的方法。一個返回最佳單一報價,另一個返回所有報價。不知何故,在下游,我們需要知道呼叫者需要什麼型別的回覆。在訊息架構中實現這一點的最佳方法是使用一些描述您意圖的元資料來豐富訊息內容。內容豐富器就是解決此問題的模式之一。Spring Integration 作為一種便利,確實提供了一個單獨的配置元素來用任意資料豐富訊息頭(稍後描述)。然而,由於 gateway 元素負責構造初始訊息,因此它包含了用任意訊息頭豐富新建立訊息的能力。在我們的示例中,每當呼叫 getBestQuote() 方法時,我們都會新增一個 RESPONSE_TYPE 頭,其值為 BEST。對於其他方法,我們不新增任何頭。現在我們可以在下游檢查此頭的存在。根據它的存在及其值,我們可以確定呼叫者想要什麼型別的回覆。
根據用例,我們還知道需要執行一些預篩選步驟,例如獲取和評估消費者的信用評分,因為一些頂級銀行只接受滿足最低信用評分要求的消費者的報價請求。因此,如果訊息在轉發給銀行之前能用此類資訊進行豐富,那就太好了。如果需要完成多個過程才能提供此類元資訊,並且這些過程可以分組為一個單元,那也很好。在我們的用例中,我們需要確定信用評分,並根據信用評分和一些規則,選擇要傳送報價請求的訊息通道列表(銀行通道)。
組合訊息處理器
組合訊息處理器模式描述了構建對訊息流保持控制的端點的規則,訊息流由多個訊息處理器組成。在 Spring Integration 中,組合訊息處理器模式由 <chain> 元素實現。
下圖顯示了鏈模式
上圖顯示我們有一個鏈,其中包含一個內部 header-enricher 元素,該元素進一步使用 CREDIT_SCORE 訊息頭和值(由對信用服務(一個簡單的 POJO Spring bean,由“creditBureau”名稱標識)的呼叫確定)豐富訊息內容。然後它委派給訊息路由器。
下圖顯示了訊息路由器模式
Spring Integration 提供了訊息路由模式的多種實現。在這種情況下,我們使用一個路由器,它透過評估一個表示式(使用 Spring Expression Language)來確定通道列表,該表示式檢視信用評分(在前一步中確定)並根據信用評分的值從 ID 為 banks 的 Map Bean 中選擇值為 premier 或 secondary 的通道列表。一旦選擇了通道列表,訊息就會路由到這些通道。
現在,最後一件事是貸款經紀人需要從銀行接收貸款報價,按消費者聚合它們(我們不想向一個消費者展示另一個消費者的報價),根據消費者的選擇標準(單個最佳報價或所有報價)組裝響應,並將回覆傳送給消費者。
下圖顯示了訊息聚合器模式
聚合器模式描述了一個將相關訊息分組為單個訊息的端點。可以提供標準和規則來確定聚合和關聯策略。Spring Integration 提供了聚合器模式的多種實現以及方便的基於名稱空間的配置。
以下示例顯示瞭如何定義聚合器
<int:aggregator id="quotesAggregator"
input-channel="quotesAggregationChannel"
method="aggregateQuotes">
<beans:bean class="org.springframework.integration.samples.loanbroker.LoanQuoteAggregator"/>
</int:aggregator>
我們的貸款經紀人使用 <aggregator> 元素定義了一個 'quotesAggregator' bean,它提供了一個預設的聚合和關聯策略。預設的關聯策略根據 correlationId 頭關聯訊息(請參閱 EIP 書中的關聯識別符號模式)。請注意,我們從未為該頭提供值。它之前由路由器在為每個銀行通道生成單獨的訊息時自動設定。
一旦訊息被關聯,它們就會被釋放到實際的聚合器實現中。雖然 Spring Integration 提供了一個預設聚合器,但它的策略(收集所有訊息的有效負載列表,並使用此列表作為其有效負載構造一個新訊息)不滿足我們的要求。將所有結果放在訊息中是一個問題,因為我們的消費者可能需要單一的最佳報價或所有報價。為了傳達消費者的意圖,我們在流程早期設定了 RESPONSE_TYPE 頭。現在我們必須評估此頭並返回所有報價(預設聚合策略將起作用)或最佳報價(預設聚合策略不起作用,因為我們必須確定哪個貸款報價是最佳的)。
在更實際的應用程式中,選擇最佳報價可能基於複雜的標準,這可能會影響聚合器實現和配置的複雜性。然而,目前我們正在將其簡化。如果消費者想要最佳報價,我們選擇利率最低的報價。為了實現這一點,LoanQuoteAggregator 類按利率對所有報價進行排序並返回第一個。LoanQuote 類實現了 Comparable 以根據利率屬性比較報價。一旦建立了響應訊息,它就會發送到啟動該過程的訊息閘道器的預設回覆通道(從而傳送給消費者)。我們的消費者獲得了貸款報價!
總而言之,一個相當複雜的過程是基於 POJO(即現有或遺留)邏輯和輕量級、可嵌入的訊息傳遞框架(Spring Integration)組裝的,它採用松耦合的程式設計模型,旨在簡化異構系統的整合,而無需重量級的 ESB 類引擎或專有的開發和部署環境。作為開發人員,您不應該僅僅因為有整合問題而需要將您的 Swing 或基於控制檯的應用程式移植到 ESB 類伺服器或實現專有介面。
本節中的此示例和其他示例均基於企業整合模式構建。您可以將它們視為解決方案的“構建塊”。它們並非旨在成為完整的解決方案。整合問題存在於所有型別的應用程式中(無論是否基於伺服器)。我們的目標是使整合應用程式不需要更改設計、測試和部署策略。
咖啡館示例
本節涵蓋 Spring Integration 示例中包含的咖啡館示例應用程式。此示例的靈感來自 Gregor Hohpe 的 Ramblings 中另一個精選的示例。
領域是咖啡館,下圖描繪了基本流程
Order 物件可能包含多個 OrderItem。一旦訂單下達,拆分器會將複合訂單訊息拆分為每杯飲料的單個訊息。然後,路由器會處理這些訊息,路由器透過檢查 OrderItem 物件的“isIced”屬性來確定飲料是熱飲還是冷飲。Barista 會準備每杯飲料,但熱飲和冷飲的準備由兩種不同的方法處理:“prepareHotDrink”和“prepareColdDrink”。準備好的飲料隨後會發送到 Waiter,在那裡它們被聚合到一個 Delivery 物件中。
以下清單顯示了 XML 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:int="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream
https://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
<int:gateway id="cafe" service-interface="o.s.i.samples.cafe.Cafe"/>
<int:channel id="orders"/>
<int:splitter input-channel="orders" ref="orderSplitter"
method="split" output-channel="drinks"/>
<int:channel id="drinks"/>
<int:router input-channel="drinks"
ref="drinkRouter" method="resolveOrderItemChannel"/>
<int:channel id="coldDrinks"><int:queue capacity="10"/></int:channel>
<int:service-activator input-channel="coldDrinks" ref="barista"
method="prepareColdDrink" output-channel="preparedDrinks"/>
<int:channel id="hotDrinks"><int:queue capacity="10"/></int:channel>
<int:service-activator input-channel="hotDrinks" ref="barista"
method="prepareHotDrink" output-channel="preparedDrinks"/>
<int:channel id="preparedDrinks"/>
<int:aggregator input-channel="preparedDrinks" ref="waiter"
method="prepareDelivery" output-channel="deliveries"/>
<int-stream:stdout-channel-adapter id="deliveries"/>
<beans:bean id="orderSplitter"
class="org.springframework.integration.samples.cafe.xml.OrderSplitter"/>
<beans:bean id="drinkRouter"
class="org.springframework.integration.samples.cafe.xml.DrinkRouter"/>
<beans:bean id="barista" class="o.s.i.samples.cafe.xml.Barista"/>
<beans:bean id="waiter" class="o.s.i.samples.cafe.xml.Waiter"/>
<int:poller id="poller" default="true" fixed-rate="1000"/>
</beans:beans>
每個訊息端點連線到輸入通道、輸出通道或兩者。每個端點都管理自己的生命週期(預設情況下,端點在初始化時自動啟動,為防止這種情況,請新增 auto-startup 屬性並將其值設定為 false)。最重要的是,請注意物件是具有強型別方法引數的簡單 POJO。以下示例顯示了拆分器
public class OrderSplitter {
public List<OrderItem> split(Order order) {
return order.getItems();
}
}
對於路由器,返回值不必是 MessageChannel 例項(儘管它可以是)。在此示例中,返回一個包含通道名稱的 String 值,如下面清單所示。
public class DrinkRouter {
public String resolveOrderItemChannel(OrderItem orderItem) {
return (orderItem.isIced()) ? "coldDrinks" : "hotDrinks";
}
}
現在,回到 XML,您可以看到有兩個 <service-activator> 元素。每個都委託給相同的 Barista 例項,但使用不同的方法(prepareHotDrink 或 prepareColdDrink),每個都對應於訂單項已路由到的兩個通道之一。以下清單顯示了 Barista 類(包含 prepareHotDrink 和 prepareColdDrink 方法)
public class Barista {
private long hotDrinkDelay = 5000;
private long coldDrinkDelay = 1000;
private AtomicInteger hotDrinkCounter = new AtomicInteger();
private AtomicInteger coldDrinkCounter = new AtomicInteger();
public void setHotDrinkDelay(long hotDrinkDelay) {
this.hotDrinkDelay = hotDrinkDelay;
}
public void setColdDrinkDelay(long coldDrinkDelay) {
this.coldDrinkDelay = coldDrinkDelay;
}
public Drink prepareHotDrink(OrderItem orderItem) {
try {
Thread.sleep(this.hotDrinkDelay);
System.out.println(Thread.currentThread().getName()
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
+ " for order #" + orderItem.getOrder().getNumber()
+ ": " + orderItem);
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
orderItem.isIced(), orderItem.getShots());
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
public Drink prepareColdDrink(OrderItem orderItem) {
try {
Thread.sleep(this.coldDrinkDelay);
System.out.println(Thread.currentThread().getName()
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet()
+ " for order #" + orderItem.getOrder().getNumber() + ": "
+ orderItem);
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
orderItem.isIced(), orderItem.getShots());
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
}
從上面的程式碼片段中可以看到,Barista 方法具有不同的延遲(熱飲的準備時間是冷飲的五倍)。這模擬了以不同速率完成的工作。當 CafeDemo 的“main”方法執行時,它迴圈 100 次,每次傳送一杯熱飲和一杯冷飲。它實際上是透過呼叫 Cafe 介面上的“placeOrder”方法來發送訊息的。在前面的 XML 配置中,您可以看到指定了 <gateway> 元素。這會觸發建立實現給定服務介面並將其連線到通道的代理。通道名稱在 Cafe 介面的 @Gateway 註解上提供,如下面的介面定義所示
public interface Cafe {
@Gateway(requestChannel="orders")
void placeOrder(Order order);
}
最後,看一下 CafeDemo 本身的 main() 方法
public static void main(String[] args) {
AbstractApplicationContext context = null;
if (args.length > 0) {
context = new FileSystemXmlApplicationContext(args);
}
else {
context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
}
Cafe cafe = context.getBean("cafe", Cafe.class);
for (int i = 1; i <= 100; i++) {
Order order = new Order(i);
order.addItem(DrinkType.LATTE, 2, false);
order.addItem(DrinkType.MOCHA, 3, true);
cafe.placeOrder(order);
}
}
要執行此示例以及其他八個示例,請參閱主發行版 samples 目錄中的 README.txt(如本章開頭所述)。 |
當您執行 cafeDemo 時,您會看到冷飲最初比熱飲準備得更快。因為存在一個聚合器,所以冷飲實際上受到熱飲準備速度的限制。這是意料之中的,基於它們各自 1000 和 5000 毫秒的延遲。然而,透過配置一個帶有併發任務執行器的輪詢器,您可以顯著改變結果。例如,您可以為熱飲咖啡師使用一個帶有五個工作執行緒的執行緒池執行器,而冷飲咖啡師保持不變。以下清單配置了這樣的安排
<int:service-activator input-channel="hotDrinks"
ref="barista"
method="prepareHotDrink"
output-channel="preparedDrinks"/>
<int:service-activator input-channel="hotDrinks"
ref="barista"
method="prepareHotDrink"
output-channel="preparedDrinks">
<int:poller task-executor="pool" fixed-rate="1000"/>
</int:service-activator>
<task:executor id="pool" pool-size="5"/>
此外,請注意每次呼叫都會顯示工作執行緒名稱。您可以看到熱飲是由任務執行器執行緒準備的。如果您提供一個短得多的輪詢間隔(例如 100 毫秒),您會發現它偶爾會透過強制任務排程程式(呼叫者)呼叫操作來限制輸入。
除了嘗試輪詢器的併發設定外,您還可以新增“transactional”子元素,然後引用上下文中的任何 PlatformTransactionManager 例項。 |
XML 訊息傳遞示例
basic/xml 中的 XML 訊息傳遞示例展示瞭如何使用一些提供的處理 XML 有效負載的元件。該示例使用了處理表示為 XML 的書籍訂單的想法。
此示例顯示名稱空間字首可以隨心所欲。雖然我們通常將 int-xml 用於整合 XML 元件,但該示例使用 si-xml。(int 是“Integration”的縮寫,si 是“Spring Integration”的縮寫。) |
首先,訂單被拆分成若干訊息,每條訊息代表 XPath 拆分器元件中的一個訂單項。以下清單顯示了拆分器的配置
<si-xml:xpath-splitter id="orderItemSplitter" input-channel="ordersChannel"
output-channel="stockCheckerChannel" create-documents="true">
<si-xml:xpath-expression expression="/orderNs:order/orderNs:orderItem"
namespace-map="orderNamespaceMap" />
</si-xml:xpath-splitter>
然後,服務啟用器將訊息傳遞給庫存檢查器 POJO。訂單項文件會透過庫存檢查器關於訂單項庫存級別的資訊進行豐富。然後,此豐富後的訂單項訊息用於路由訊息。如果訂單項有庫存,訊息將路由到倉庫。以下清單配置了路由訊息的 xpath-router
<si-xml:xpath-router id="inStockRouter" input-channel="orderRoutingChannel" resolution-required="true">
<si-xml:xpath-expression expression="/orderNs:orderItem/@in-stock" namespace-map="orderNamespaceMap" />
<si-xml:mapping value="true" channel="warehouseDispatchChannel"/>
<si-xml:mapping value="false" channel="outOfStockChannel"/>
</si-xml:xpath-router>
當訂單項缺貨時,訊息會透過 XSLT 轉換為適合傳送給供應商的格式。以下清單配置了 XSLT 轉換器
<si-xml:xslt-transformer input-channel="outOfStockChannel"
output-channel="resupplyOrderChannel"
xsl-resource="classpath:org/springframework/integration/samples/xml/bigBooksSupplierTransformer.xsl"/>