Spring Integration 框架概述
Spring Integration 提供了 Spring 程式設計模型的擴充套件,以支援眾所周知的 企業整合模式。它在基於 Spring 的應用程式中實現了輕量級訊息傳遞,並透過宣告式介面卡支援與外部系統的整合。這些介面卡提供了比 Spring 對遠端處理、訊息傳遞和排程支援更高層次的抽象。
Spring Integration 的主要目標是為構建企業整合解決方案提供一個簡單的模型,同時保持對於生成可維護、可測試程式碼至關重要的關注點分離。
Spring Integration 概述
本章對 Spring Integration 的核心概念和元件進行了高階介紹。它包含一些程式設計技巧,可幫助您充分利用 Spring Integration。
背景
Spring Framework 的關鍵主題之一是控制反轉 (IoC)。從廣義上講,這意味著框架代表在其上下文中管理的元件處理職責。元件本身得到簡化,因為它們擺脫了這些職責。例如,依賴注入將元件從定位或建立其依賴項的責任中解放出來。同樣,面向切面程式設計透過將通用橫切關注點模組化為可重用切面,將業務元件從這些關注點中解放出來。在每種情況下,最終結果都是一個更容易測試、理解、維護和擴充套件的系統。
此外,Spring 框架和產品組合為構建企業應用程式提供了全面的程式設計模型。開發人員受益於該模型的一致性,尤其是它基於公認的最佳實踐,例如面向介面程式設計和優先使用組合而非繼承。Spring 簡化的抽象和強大的支援庫提高了開發人員的工作效率,同時提高了可測試性和可移植性。
Spring Integration 的動機與這些目標和原則相同。它將 Spring 程式設計模型擴充套件到訊息傳遞領域,並建立在 Spring 現有企業整合支援的基礎上,提供更高層次的抽象。它支援訊息驅動架構,其中控制反轉適用於執行時關注點,例如何時應執行某些業務邏輯以及應將響應傳送到何處。它支援訊息的路由和轉換,以便可以整合不同的傳輸和不同的資料格式,而不會影響可測試性。換句話說,訊息傳遞和整合關注點由框架處理。業務元件進一步與基礎設施隔離,開發人員擺脫了複雜的整合職責。
作為 Spring 程式設計模型的擴充套件,Spring Integration 提供了各種配置選項,包括註解、帶有名稱空間支援的 XML、帶有通用“bean”元素的 XML 以及直接使用底層 API。該 API 基於定義明確的策略介面和非侵入式、委託介面卡。Spring Integration 的設計靈感來自於 Spring 中常見模式與 Gregor Hohpe 和 Bobby Woolf (Addison Wesley, 2004) 所著《企業整合模式》中描述的著名模式之間存在密切關聯的認識。讀過那本書的開發人員應該會立即熟悉 Spring Integration 的概念和術語。
目標和原則
Spring Integration 的目標如下:
-
提供一個簡單的模型來實現複雜的企業整合解決方案。
-
促進基於 Spring 的應用程式中的非同步、訊息驅動行為。
-
促進現有 Spring 使用者直觀、漸進式的採用。
Spring Integration 遵循以下原則:
-
元件應鬆散耦合,以實現模組化和可測試性。
-
框架應強制分離業務邏輯和整合邏輯的關注點。
-
擴充套件點應具有抽象性質(但在明確定義的邊界內),以促進重用和可移植性。
主要元件
從垂直角度看,分層架構有助於關注點分離,層間基於介面的契約促進了鬆散耦合。基於 Spring 的應用程式通常以此方式設計,Spring 框架和產品組合為遵循企業應用程式整個堆疊的這一最佳實踐提供了堅實的基礎。訊息驅動架構增加了水平視角,但這些目標仍然相關。就像“分層架構”是一個極其通用和抽象的正規化一樣,訊息傳遞系統通常遵循類似的抽象“管道和過濾器”模型。“過濾器”表示能夠生產或消費訊息的任何元件,“管道”在過濾器之間傳輸訊息,以便元件本身保持鬆散耦合。重要的是要注意,這兩種高階正規化並非相互排斥。支援“管道”的底層訊息傳遞基礎設施仍應封裝在一個層中,其契約定義為介面。同樣,“過濾器”本身應在應用程式服務層邏輯上方的層中進行管理,以與 Web 層大致相同的方式透過介面與這些服務進行互動。
訊息
在 Spring Integration 中,訊息是任何 Java 物件的通用包裝器,並結合了框架在處理該物件時使用的元資料。它由有效載荷和標頭組成。有效載荷可以是任何型別,標頭包含常用資訊,例如 ID、時間戳、關聯 ID 和返回地址。標頭還用於將值傳遞給連線的傳輸和從連線的傳輸傳遞值。例如,當從收到的檔案建立訊息時,檔名可以儲存在標頭中,供下游元件訪問。同樣,如果訊息的內容最終將由出站郵件介面卡傳送,則各種屬性(收件人、發件人、抄送、主題等)可以由上游元件配置為訊息標頭值。開發人員還可以將任意鍵值對儲存在標頭中。
訊息通道
訊息通道代表管道和過濾器架構中的“管道”。生產者將訊息傳送到通道,消費者從通道接收訊息。因此,訊息通道解耦了訊息傳遞元件,併為訊息的攔截和監控提供了一個方便點。
訊息通道可以遵循點對點或釋出-訂閱語義。對於點對點通道,傳送到通道的每條訊息最多隻能由一個消費者接收。另一方面,釋出-訂閱通道嘗試將每條訊息廣播給通道上的所有訂閱者。Spring Integration 支援這兩種模型。
雖然“點對點”和“釋出-訂閱”定義了最終有多少消費者接收每條訊息的兩種選項,但還有一個重要考慮因素:通道是否應該緩衝訊息?在 Spring Integration 中,可輪詢通道能夠將訊息緩衝在佇列中。緩衝的優點是它允許限制入站訊息,從而防止消費者過載。然而,正如名稱所示,這也增加了一些複雜性,因為消費者只有在配置了輪詢器的情況下才能從這樣的通道接收訊息。另一方面,連線到可訂閱通道的消費者只是訊息驅動的。訊息通道實現詳細討論了 Spring Integration 中可用的各種通道實現。
訊息端點
Spring Integration 的主要目標之一是透過控制反轉簡化企業整合解決方案的開發。這意味著您不應該直接實現消費者和生產者,甚至不應該構建訊息並呼叫訊息通道上的傳送或接收操作。相反,您應該能夠專注於自己的特定領域模型,並使用基於普通物件的實現。然後,透過提供宣告性配置,您可以將您的領域特定程式碼“連線”到 Spring Integration 提供的訊息傳遞基礎設施。負責這些連線的元件是訊息端點。這並不意味著您必須直接連線現有應用程式程式碼。任何實際的企業整合解決方案都需要一定數量的程式碼專注於整合關注點,例如路由和轉換。重要的是在整合邏輯和業務邏輯之間實現關注點分離。換句話說,就像 Web 應用程式的 Model-View-Controller (MVC) 正規化一樣,目標應該是提供一個薄但專用的層,將入站請求轉換為服務層呼叫,然後將服務層返回值轉換為出站回覆。下一節概述了處理這些職責的訊息端點型別,在後續章節中,您將看到 Spring Integration 的宣告性配置選項如何提供一種非侵入式的方式來使用其中每種型別。
訊息端點
訊息端點代表了管道和過濾器架構中的“過濾器”。如前所述,端點的主要作用是將應用程式程式碼連線到訊息傳遞框架,並以非侵入式方式實現。換句話說,理想情況下,應用程式程式碼不應該知道訊息物件或訊息通道。這類似於 MVC 正規化中控制器的作用。正如控制器處理 HTTP 請求一樣,訊息端點處理訊息。正如控制器對映到 URL 模式一樣,訊息端點對映到訊息通道。這兩種情況下的目標都是相同的:將應用程式程式碼與基礎設施隔離。這些概念和所有後續模式在《企業整合模式》一書中都有詳細討論。在這裡,我們僅對 Spring Integration 支援的主要端點型別及其相關角色進行高階描述。後續章節將進行闡述並提供示例程式碼和配置示例。
訊息轉換器
訊息轉換器負責轉換訊息的內容或結構並返回修改後的訊息。最常見的轉換器型別可能是將訊息的有效載荷從一種格式轉換為另一種格式(例如從 XML 轉換為 java.lang.String)。同樣,轉換器可以新增、刪除或修改訊息的標頭值。
訊息過濾器
訊息過濾器決定是否應將訊息傳遞到輸出通道。這隻需一個布林測試方法,該方法可以檢查特定的有效載荷內容型別、屬性值、標頭的存在或其他條件。如果訊息被接受,它將傳送到輸出通道。如果未接受,它將被丟棄(或者,對於更嚴格的實現,可以丟擲 Exception)。訊息過濾器通常與釋出-訂閱通道結合使用,其中多個消費者可能會接收相同的訊息,並使用過濾器的條件來縮小要處理的訊息集。
| 請注意不要將管道和過濾器架構模式中“過濾器”的通用用法與這種特定的端點型別混淆,後者選擇性地縮小了兩個通道之間流動的訊息範圍。管道和過濾器概念中的“過濾器”與 Spring Integration 的訊息端點更匹配:任何可以連線到訊息通道以傳送或接收訊息的元件。 |
訊息路由器
訊息路由器負責決定接下來哪個或哪些通道(如果有)應該接收訊息。通常,該決定基於訊息的內容或訊息頭中可用的元資料。訊息路由器通常用作服務啟用器或其他能夠傳送回覆訊息的端點上靜態配置的輸出通道的動態替代方案。同樣,訊息路由器為多個訂閱者使用的響應式訊息過濾器提供了一種主動替代方案,如前所述。
聚合器
聚合器基本上是拆分器的映象,它是一種訊息端點,接收多條訊息並將它們組合成一條訊息。事實上,聚合器通常是包含拆分器的管道中的下游消費者。從技術上講,聚合器比拆分器更復雜,因為它需要維護狀態(要聚合的訊息),決定何時所有訊息都可用,並在必要時超時。此外,如果發生超時,聚合器需要知道是傳送部分結果、丟棄它們,還是將它們傳送到單獨的通道。Spring Integration 提供了 CorrelationStrategy、ReleaseStrategy 以及用於超時、是否在超時時傳送部分結果以及丟棄通道的可配置設定。
服務啟用器
服務啟用器是用於將服務例項連線到訊息系統的通用端點。必須配置輸入訊息通道,並且如果將呼叫的服務方法能夠返回值,也可以提供輸出訊息通道。
| 輸出通道是可選的,因為每條訊息也可以提供自己的“返回地址”標頭。此規則適用於所有消費者端點。 |
服務啟用器呼叫某個服務物件上的操作來處理請求訊息,提取請求訊息的有效負載並進行轉換(如果該方法不期望訊息型別的引數)。只要服務物件的方法返回值,該返回值也會在必要時轉換為回覆訊息(如果它本身不是訊息型別)。該回復訊息將傳送到輸出通道。如果未配置輸出通道,則回覆將傳送到訊息“返回地址”中指定的通道(如果可用)。
請求-回覆服務啟用器端點將目標物件的方法連線到輸入和輸出訊息通道。
| 如前所述,在訊息通道中,通道可以是可輪詢的或可訂閱的。在上述圖中,這透過“時鐘”符號和實心箭頭(輪詢)以及虛線箭頭(訂閱)表示。 |
通道介面卡
通道介面卡是將訊息通道連線到其他系統或傳輸的端點。通道介面卡可以是入站或出站的。通常,通道介面卡會在訊息與從其他系統(檔案、HTTP 請求、JMS 訊息等)接收或傳送到其他系統的任何物件或資源之間進行一些對映。根據傳輸方式,通道介面卡還可以填充或提取訊息頭值。Spring Integration 提供了許多通道介面卡,這些介面卡將在後續章節中介紹。
MessageChannel。| 訊息源可以是可輪詢的(例如 POP3)或訊息驅動的(例如 IMAP Idle)。在上述圖中,這透過“時鐘”符號和實心箭頭(輪詢)以及虛線箭頭(訊息驅動)表示。 |
MessageChannel 連線到目標系統。| 如前所述,在訊息通道中,通道可以是可輪詢的或可訂閱的。在上述圖中,這透過“時鐘”符號和實心箭頭(輪詢)以及虛線箭頭(訂閱)表示。 |
端點 Bean 名稱
消費端點(任何帶有 inputChannel 的端點)由兩個 bean 組成:消費者和訊息處理器。消費者引用訊息處理器,並在訊息到達時呼叫它。
考慮以下 XML 示例:
<int:service-activator id = "someService" ... />
根據上述示例,bean 名稱如下:
-
消費者:
someService(id) -
處理器:
someService.handler
使用企業整合模式 (EIP) 註解時,名稱取決於幾個因素。考慮以下帶註解 POJO 的示例:
@Component
public class SomeComponent {
@ServiceActivator(inputChannel = ...)
public String someMethod(...) {
...
}
}
根據上述示例,bean 名稱如下:
-
消費者:
someComponent.someMethod.serviceActivator -
處理器:
someComponent.someMethod.serviceActivator.handler
從版本 5.0.4 開始,您可以使用 @EndpointId 註解修改這些名稱,如以下示例所示:
@Component
public class SomeComponent {
@EndpointId("someService")
@ServiceActivator(inputChannel = ...)
public String someMethod(...) {
...
}
}
根據上述示例,bean 名稱如下:
-
消費者:
someService -
處理器:
someService.handler
@EndpointId 建立的名稱與 XML 配置中的 id 屬性建立的名稱相同。考慮以下帶註解 bean 的示例:
@Configuration
public class SomeConfiguration {
@Bean
@ServiceActivator(inputChannel = ...)
public MessageHandler someHandler() {
...
}
}
根據上述示例,bean 名稱如下:
-
消費者:
someConfiguration.someHandler.serviceActivator -
處理器:
someHandler(@Bean名稱)
從版本 5.0.4 開始,您可以使用 @EndpointId 註解修改這些名稱,如以下示例所示:
@Configuration
public class SomeConfiguration {
@Bean("someService.handler") (1)
@EndpointId("someService") (2)
@ServiceActivator(inputChannel = ...)
public MessageHandler someHandler() {
...
}
}
| 1 | 處理器:someService.handler(bean 名稱) |
| 2 | 消費者:someService(端點 ID) |
@EndpointId 註解建立的名稱與 XML 配置中的 id 屬性建立的名稱相同,只要您遵循將 .handler 附加到 @Bean 名稱的約定。
有一種特殊情況會建立第三個 bean:出於架構原因,如果 MessageHandler @Bean 未定義 AbstractReplyProducingMessageHandler,框架會將提供的 bean 包裝在 ReplyProducingMessageHandlerWrapper 中。此包裝器支援請求處理器建議處理併發出正常的“未產生回覆”除錯日誌訊息。其 bean 名稱是處理器 bean 名稱加上 .wrapper(當存在 @EndpointId 時——否則,它是正常生成的處理器名稱)。
同樣,可輪詢訊息源會建立兩個 bean,一個 SourcePollingChannelAdapter (SPCA) 和一個 MessageSource。
考慮以下 XML 配置:
<int:inbound-channel-adapter id = "someAdapter" ... />
根據上述 XML 配置,bean 名稱如下:
-
SPCA:
someAdapter(id) -
處理器:
someAdapter.source
考慮以下 POJO 的 Java 配置,以定義 @EndpointId:
@EndpointId("someAdapter")
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
public String pojoSource() {
...
}
根據上述 Java 配置示例,bean 名稱如下:
-
SPCA:
someAdapter -
處理器:
someAdapter.source
考慮以下 Bean 的 Java 配置,以定義 @EndpointID:
@Bean("someAdapter.source")
@EndpointId("someAdapter")
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
public MessageSource<?> source() {
return () -> {
...
};
}
根據上述示例,bean 名稱如下:
-
SPCA:
someAdapter -
處理器:
someAdapter.source(只要您遵循將.source附加到@Bean名稱的約定)
配置和 @EnableIntegration
在本文件中,您將看到對 XML 名稱空間支援的引用,用於在 Spring Integration 流中宣告元素。此支援由一系列名稱空間解析器提供,這些解析器生成適當的 bean 定義以實現特定元件。例如,許多端點由一個 MessageHandler bean 和一個 ConsumerEndpointFactoryBean 組成,其中注入了處理器和輸入通道名稱。
首次遇到 Spring Integration 名稱空間元素時,框架會自動宣告許多 bean(任務排程器、隱式通道建立器等),這些 bean 用於支援執行時環境。
版本 4.0 引入了 @EnableIntegration 註解,允許註冊 Spring Integration 基礎設施 bean(請參閱 Javadoc)。當只使用 Java 配置時,此註解是必需的——例如,在 Spring Boot 或 Spring Integration Messaging 註解支援以及 Spring Integration Java DSL 中,沒有 XML 整合配置。 |
當您有一個沒有 Spring Integration 元件的父上下文和兩個或更多使用 Spring Integration 的子上下文時,@EnableIntegration 註解也很有用。它允許這些常見元件只在父上下文中宣告一次。
@EnableIntegration 註解嚮應用程式上下文註冊了許多基礎設施元件。具體來說,它:
-
註冊一些內建 bean,例如
errorChannel及其LoggingHandler、用於輪詢器的taskScheduler、jsonPathSpEL 函式等。 -
新增多個
BeanFactoryPostProcessor例項,以增強BeanFactory以支援全域性和預設整合環境。 -
新增多個
BeanPostProcessor例項,以增強或轉換幷包裝特定 bean,以用於整合目的。 -
添加註解處理器,以解析訊息註解並嚮應用程式上下文註冊其元件。
@IntegrationComponentScan 註解還允許類路徑掃描。此註解扮演著與標準 Spring Framework @ComponentScan 註解類似的角色,但它僅限於 Spring Integration 特有的元件和註解,這些是標準 Spring Framework 元件掃描機制無法觸及的。有關示例,請參見@MessagingGateway 註解。
@EnablePublisher 註解註冊了一個 PublisherAnnotationBeanPostProcessor bean,併為那些沒有 channel 屬性的 @Publisher 註解配置了 default-publisher-channel。如果找到多個 @EnablePublisher 註解,它們必須都具有相同的預設通道值。有關更多資訊,請參閱使用 @Publisher 註解進行註解驅動配置。
@GlobalChannelInterceptor 註解已引入,用於將 ChannelInterceptor bean 標記為全域性通道攔截。此註解是 <int:channel-interceptor> XML 元素的模擬(請參閱全域性通道攔截器配置)。@GlobalChannelInterceptor 註解可以放置在類級別(帶有 @Component 構造型註解)或 @Configuration 類中的 @Bean 方法上。在這兩種情況下,bean 都必須實現 ChannelInterceptor。
從版本 5.1 開始,全域性通道攔截器適用於動態註冊的通道——例如,透過 beanFactory.initializeBean() 或在使用 Java DSL 時透過 IntegrationFlowContext 初始化的 bean。以前,在應用程式上下文重新整理後建立 bean 時,攔截器不適用。
@IntegrationConverter 註解將 Converter、GenericConverter 或 ConverterFactory bean 標記為 integrationConversionService 的候選轉換器。此註解是 <int:converter> XML 元素的模擬(請參閱有效載荷型別轉換)。您可以將 @IntegrationConverter 註解放置在類級別(帶有 @Component 構造型註解)或 @Configuration 類中的 @Bean 方法上。
有關訊息註解的更多資訊,請參閱註解支援。
程式設計注意事項
Spring Integration 中的大多數類,除非另有說明,都必須在應用程式上下文中宣告為 bean,並且為單例。這意味著這些類的例項是執行緒安全的,它們的生命週期以及與其他元件的連線由 Spring 依賴注入容器管理。實用程式和構建器類(JacksonMessagingUtils、MessageBuilder、ExpressionEvalMap、IntegrationReactiveUtils 等)可以直接在 Java 程式碼中使用。但是,Java DSL 工廠和 IntegrationComponentSpec 實現結果仍必須註冊為 bean 到應用程式上下文中。Session 抽象存在於許多模組中,它不是執行緒安全的,通常由 Factory 模式實現建立,並從執行緒安全的 Template 模式中使用。例如,請參見 SftpRemoteFileTemplate 及其與 DefaultSftpSessionFactory 的關係。
您應該儘可能使用普通 Java 物件(POJO)(用於目標邏輯中的訊息處理),並且僅在絕對必要時才在程式碼中公開框架。有關更多資訊,請參閱POJO 方法呼叫。
如果您的元件確實向您的類公開了框架,則需要考慮一些事項,尤其是在應用程式啟動期間:
-
如果您的元件是
ApplicationContextAware,您通常不應在setApplicationContext()方法中使用ApplicationContext。相反,請儲存一個引用,並將此類使用延遲到上下文生命週期的後期。 -
如果您的元件是
InitializingBean或使用@PostConstruct方法,請勿從這些初始化方法傳送任何訊息。在呼叫這些方法時,應用程式上下文尚未初始化,傳送此類訊息很可能會失敗。如果您需要在啟動期間傳送訊息,請實現ApplicationListener並等待ContextRefreshedEvent。或者,實現SmartLifecycle,將您的 bean 放在後期階段,並從start()方法傳送訊息。
使用打包(例如,陰影)Jar 時的注意事項
Spring Integration 使用 Spring Framework 的 SpringFactories 機制透過載入幾個 IntegrationConfigurationInitializer 類來引導某些功能。這包括 -core jar 以及其他一些 jar,包括 -http 和 -jmx。此過程的資訊儲存在每個 jar 中的 META-INF/spring.factories 檔案中。
一些開發人員喜歡使用眾所周知的工具(例如 Apache Maven Shade Plugin)將他們的應用程式和所有依賴項重新打包到單個 jar 中。
預設情況下,在生成陰影 jar 時,shade 外掛不會合並 spring.factories 檔案。
除了 spring.factories,其他 META-INF 檔案(spring.handlers 和 spring.schemas)用於 XML 配置。這些檔案也需要合併。
Spring Boot 的可執行 jar 機制採用了一種不同的方法,它嵌套了 jar,從而保留了類路徑上的每個 spring.factories 檔案。因此,對於 Spring Boot 應用程式,如果您使用其預設的可執行 jar 格式,則無需額外操作。 |
即使您不使用 Spring Boot,您仍然可以使用 Boot 提供的工具透過為上述檔案新增轉換器來增強 shade 外掛。以下示例顯示瞭如何配置外掛:
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
<createDependencyReducedPom>true</createDependencyReducedPom>
</configuration>
<dependencies>
<dependency> (1)
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers> (2)
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer
implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
...
具體來說,
| 1 | 新增 spring-boot-maven-plugin 作為依賴項。 |
| 2 | 配置轉換器。 |
您可以為 ${spring.boot.version} 新增一個屬性或使用顯式版本。
程式設計技巧
本節介紹了一些充分利用 Spring Integration 的方法。
XML 模式
使用 XML 配置時,為避免出現錯誤的模式驗證錯誤,您應該使用“Spring 感知”IDE,例如 Spring Tool Suite (STS)、帶有 Spring IDE 外掛的 Eclipse 或 IntelliJ IDEA。這些 IDE 知道如何從類路徑解析正確的 XML 模式(透過使用 jar 中的 META-INF/spring.schemas 檔案)。在使用 STS 或帶有外掛的 Eclipse 時,您必須在專案上啟用 Spring Project Nature。
由於相容性原因,某些舊模組(1.0 版中存在的模組)在網際網路上託管的模式是 1.0 版。如果您的 IDE 使用這些模式,您可能會看到錯誤。
這些線上模式中的每一個都包含一個類似於以下內容的警告:
|
此模式適用於 Spring Integration Core 的 1.0 版本。我們無法將其更新到當前模式,因為那會破壞使用 1.0.3 或更低版本的所有應用程式。對於後續版本,“無版本”模式將從類路徑解析並從 jar 中獲取。請參考 GitHub |
受影響的模組是:
-
核心(spring-integration.xsd) -
file -
http -
jms -
mail -
security -
stream -
ws -
xml
查詢 Java 和 DSL 配置的類名
透過 XML 配置和 Spring Integration 名稱空間支援,XML 解析器隱藏了目標 bean 如何宣告和連線在一起。對於 Java 配置,瞭解目標終端使用者應用程式的框架 API 非常重要。
EIP 實現的首要元件是 Message、Channel 和 Endpoint(請參閱本章前面的主要元件)。它們的實現(契約)是:
前兩個足夠簡單,易於理解如何實現、配置和使用。最後一個值得更多關注:
AbstractEndpoint 在整個 Spring 框架中廣泛用於不同的元件實現。其主要實現是:
-
EventDrivenConsumer,當我們訂閱SubscribableChannel以監聽訊息時使用。 -
PollingConsumer,當我們從PollableChannel輪詢訊息時使用。
當您使用訊息註解或 Java DSL 時,您無需擔心這些元件,因為框架會自動使用適當的註解和 BeanPostProcessor 實現來生成它們。手動構建元件時,您應該使用 ConsumerEndpointFactoryBean 來幫助根據提供的 inputChannel 屬性確定要建立的目標 AbstractEndpoint 消費者實現。
另一方面,ConsumerEndpointFactoryBean 委託給框架中的另一個核心元件 - org.springframework.messaging.MessageHandler。此介面實現的目標是處理由端點從通道消費的訊息。Spring Integration 中的所有 EIP 元件都是 MessageHandler 實現(例如,AggregatingMessageHandler、MessageTransformingHandler、AbstractMessageSplitter 等)。目標協議出站介面卡(FileWritingMessageHandler、HttpRequestExecutingMessageHandler、AbstractMqttMessageHandler 等)也是 MessageHandler 實現。當您使用 Java 配置開發 Spring Integration 應用程式時,您應該檢視 Spring Integration 模組以查詢適當的 MessageHandler 實現以用於 @ServiceActivator 配置。例如,要傳送 XMPP 訊息(請參閱XMPP 支援),您應該配置如下內容:
@Bean
@ServiceActivator(inputChannel = "input")
public MessageHandler sendChatMessageHandler(XMPPConnection xmppConnection) {
ChatMessageSendingMessageHandler handler = new ChatMessageSendingMessageHandler(xmppConnection);
DefaultXmppHeaderMapper xmppHeaderMapper = new DefaultXmppHeaderMapper();
xmppHeaderMapper.setRequestHeaderNames("*");
handler.setHeaderMapper(xmppHeaderMapper);
return handler;
}
MessageHandler 實現表示訊息流的出站和處理部分。
入站訊息流端有其自己的元件,分為輪詢和監聽行為。監聽(訊息驅動)元件很簡單,通常只需要一個目標類實現即可準備好生成訊息。監聽元件可以是一向的 MessageProducerSupport 實現(例如 AbstractMqttMessageDrivenChannelAdapter 和 ImapIdleChannelAdapter),也可以是請求-回覆 MessagingGatewaySupport 實現(例如 AmqpInboundGateway 和 AbstractWebServiceInboundGateway)。
輪詢入站端點適用於那些不提供監聽器 API 或不打算用於此類行為的協議,包括任何基於檔案的協議(例如 FTP)、任何資料庫(RDBMS 或 NoSQL)等。
這些入站端點由兩個元件組成:輪詢器配置,用於定期啟動輪詢任務,以及訊息源類,用於從目標協議讀取資料併為下游整合流生成訊息。用於輪詢器配置的第一類是 SourcePollingChannelAdapter。它是另一個 AbstractEndpoint 實現,但專門用於輪詢以啟動整合流。通常,使用訊息註解或 Java DSL 時,您無需擔心此類別。框架會根據 @InboundChannelAdapter 配置或 Java DSL 構建器規範為其生成一個 bean。
訊息源元件對目標應用程式開發更為重要,它們都實現了 MessageSource 介面(例如,MongoDbMessageSource 和 AbstractTwitterMessageSource)。考慮到這一點,我們從 JDBC 的 RDBMS 表中讀取資料的配置可能如下所示:
@Bean
@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixedDelay="5000"))
public MessageSource<?> storedProc(DataSource dataSource) {
return new JdbcPollingChannelAdapter(dataSource, "SELECT * FROM foo where status = 0");
}
您可以在特定的 Spring Integration 模組中(在大多數情況下,在相應的包中)找到目標協議所需的所有入站和出站類。例如,spring-integration-websocket 介面卡是:
-
o.s.i.websocket.inbound.WebSocketInboundChannelAdapter:實現MessageProducerSupport以監聽套接字上的幀並向通道生成訊息。 -
o.s.i.websocket.outbound.WebSocketOutboundMessageHandler:單向AbstractMessageHandler實現,用於將傳入訊息轉換為適當的幀並透過 WebSocket 傳送。
如果您熟悉 Spring Integration XML 配置,從版本 4.3 開始,我們在 XSD 元素定義中提供了有關用於為介面卡或閘道器宣告 bean 的目標類的資訊,如以下示例所示:
<xsd:element name="outbound-async-gateway">
<xsd:annotation>
<xsd:documentation>
Configures a Consumer Endpoint for the 'o.s.i.amqp.outbound.AsyncAmqpOutboundGateway'
that will publish an AMQP Message to the provided Exchange and expect a reply Message.
The sending thread returns immediately; the reply is sent asynchronously; uses 'AsyncRabbitTemplate.sendAndReceive()'.
</xsd:documentation>
</xsd:annotation>
POJO 方法呼叫
如程式設計注意事項中所述,我們建議使用 POJO 程式設計風格,如以下示例所示:
@ServiceActivator
public String myService(String payload) { ... }
在這種情況下,框架會提取一個 String 有效載荷,呼叫您的方法,並將結果包裝在訊息中傳送到流中的下一個元件(原始標頭會複製到新訊息中)。實際上,如果您使用 XML 配置,您甚至不需要 @ServiceActivator 註解,如以下配對示例所示:
<int:service-activator ... ref="myPojo" method="myService" />
public String myService(String payload) { ... }
只要類上的公共方法沒有歧義,您就可以省略 method 屬性。
您也可以在 POJO 方法中獲取標頭資訊,如以下示例所示:
@ServiceActivator
public String myService(@Payload String payload, @Header("foo") String fooHeader) { ... }
您還可以取消引用訊息上的屬性,如以下示例所示:
@ServiceActivator
public String myService(@Payload("payload.foo") String foo, @Header("bar.baz") String barbaz) { ... }
由於存在各種 POJO 方法呼叫,5.0 之前的版本使用 SpEL(Spring 表示式語言)來呼叫 POJO 方法。與這些方法中通常完成的實際工作相比,SpEL(甚至是解釋性的)對於這些操作通常“足夠快”。但是,從 5.0 版本開始,預設情況下儘可能使用 org.springframework.messaging.handler.invocation.InvocableHandlerMethod。這種技術通常比解釋性的 SpEL 執行速度更快,並且與 Spring 的其他訊息傳遞專案保持一致。InvocableHandlerMethod 類似於 Spring MVC 中用於呼叫控制器方法的技術。在某些情況下,仍然總是使用 SpEL 呼叫某些方法。例如,如前所述,帶有取消引用屬性的帶註解引數。這是因為 SpEL 能夠導航屬性路徑。
可能還有一些我們沒有考慮到的其他特殊情況,它們也無法與 InvocableHandlerMethod 例項一起使用。因此,在這些情況下,我們會自動回退到使用 SpEL。
如果您願意,您還可以設定您的 POJO 方法,使其始終使用 SpEL,使用 UseSpelInvoker 註解,如以下示例所示:
@UseSpelInvoker(compilerMode = "IMMEDIATE")
public void bar(String bar) { ... }
如果省略了 compilerMode 屬性,則 spring.expression.compiler.mode 系統屬性決定了編譯器模式。有關編譯後的 SpEL 的更多資訊,請參閱 SpEL 編譯。