Spring Integration Framework 概觀
Spring Integration 擴展了 Spring 程式設計模型,以支援廣為人知的 企業整合模式。它在基於 Spring 的應用程式中實現輕量級訊息傳遞,並透過宣告式配接器支援與外部系統的整合。這些配接器為 Spring 對遠端處理、訊息傳遞和排程的支援提供了更高等級的抽象。
Spring Integration 的主要目標是提供一個簡單的模型來建置企業整合解決方案,同時保持關注點分離,這對於產生可維護、可測試的程式碼至關重要。
Spring Integration 概觀
本章提供了 Spring Integration 核心概念和元件的高階介紹。它包含一些程式設計提示,可協助您充分利用 Spring Integration。
背景
Spring Framework 的關鍵主題之一是控制反轉 (IoC)。從廣義上講,這表示框架代表在其上下文中管理的元件處理責任。元件本身被簡化了,因為它們擺脫了這些責任。例如,依賴注入減輕了元件尋找或建立其依賴項的責任。同樣地,面向切面程式設計透過將通用跨領域關注點模組化為可重複使用的切面,減輕了業務元件的通用跨領域關注點。在每種情況下,最終結果都是一個更易於測試、理解、維護和擴展的系統。
此外,Spring Framework 和產品組合為建置企業應用程式提供了全面的程式設計模型。開發人員受益於此模型的一致性,尤其是它基於完善的最佳實務,例如面向介面程式設計和偏好組合勝於繼承。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 Framework 和產品組合為遵循企業應用程式完整堆疊的最佳實務提供了堅實的基礎。訊息驅動架構增加了水平視角,但這些相同的目標仍然相關。正如「分層架構」是一個極其通用且抽象的範例一樣,訊息傳遞系統通常遵循類似抽象的「管道和篩選器」模型。「篩選器」代表任何能夠產生或使用訊息的元件,「管道」在篩選器之間傳輸訊息,以便元件本身保持鬆散耦合。重要的是要注意,這兩個高階範例並非互斥。支援「管道」的底層訊息傳遞基礎架構仍應封裝在層中,其合約定義為介面。同樣地,「篩選器」本身應在邏輯上高於應用程式服務層的層中進行管理,並透過介面與這些服務互動,這與 Web 層的方式非常相似。
訊息
在 Spring Integration 中,訊息是任何 Java 物件的通用包裝器,並結合了框架在處理該物件時使用的中繼資料。它由 Payload 和標頭組成。Payload 可以是任何類型,而標頭則保存常用的必要資訊,例如 ID、時間戳記、關聯 ID 和回覆位址。標頭也用於將值傳遞到和從連接的傳輸。例如,從接收到的檔案建立訊息時,檔案名稱可能會儲存在標頭中,以供下游元件存取。同樣地,如果訊息的內容最終將透過輸出郵件配接器傳送,則各種屬性(收件人、寄件人、副本、主旨和其他)可能會由上游元件設定為訊息標頭值。開發人員也可以在標頭中儲存任何任意的鍵值對。

訊息通道
訊息通道代表管道和篩選器架構的「管道」。生產者將訊息傳送到通道,而消費者從通道接收訊息。因此,訊息通道解耦了訊息傳遞元件,並為訊息的攔截和監控提供了方便的點。

訊息通道可以遵循點對點或發佈-訂閱語意。對於點對點通道,每個傳送到通道的訊息最多只能有一個消費者接收。另一方面,發佈-訂閱通道嘗試將每個訊息廣播到通道上的所有訂閱者。Spring Integration 同時支援這兩種模型。
「點對點」和「發佈-訂閱」定義了有多少消費者最終接收每個訊息的兩個選項,還有另一個重要的考慮因素:通道是否應該緩衝訊息?在 Spring Integration 中,可輪詢通道能夠在佇列中緩衝訊息。緩衝的優點在於,它允許節流輸入訊息,從而防止消費者過載。但是,顧名思義,這也增加了一些複雜性,因為只有在設定 poller 的情況下,消費者才能從這樣的通道接收訊息。另一方面,連接到可訂閱通道的消費者只是訊息驅動的。訊息通道實作 詳細討論了 Spring Integration 中可用的各種通道實作。
訊息端點
Spring Integration 的主要目標之一是透過控制反轉簡化企業整合解決方案的開發。這表示您不應該直接實作消費者和生產者,甚至不應該建置訊息並在訊息通道上調用傳送或接收操作。相反地,您應該能夠專注於您的特定網域模型,並以基於普通物件的實作為基礎。然後,透過提供宣告式設定,您可以將您的網域特定程式碼「連接」到 Spring Integration 提供的訊息傳遞基礎架構。負責這些連接的元件是訊息端點。這並不表示您應該必然直接連接您現有的應用程式程式碼。任何真實世界的企業整合解決方案都需要一定數量的程式碼,這些程式碼專注於整合關注點,例如路由和轉換。重要的是要實現整合邏輯和業務邏輯之間的關注點分離。換句話說,與 Web 應用程式的 Model-View-Controller (MVC) 範例一樣,目標應該是提供一個薄但專用的層,將輸入請求轉換為服務層調用,然後將服務層傳回值轉換為輸出回覆。下一節概述了處理這些責任的訊息端點類型,在接下來的章節中,您可以看到 Spring Integration 的宣告式設定選項如何提供一種非侵入式的方式來使用每個端點。
訊息端點
訊息端點代表管道和篩選器架構的「篩選器」。如前所述,端點的主要作用是以非侵入式的方式將應用程式程式碼連接到訊息傳遞框架。換句話說,應用程式程式碼理想情況下應該不知道訊息物件或訊息通道。這類似於 MVC 範例中控制器的角色。正如控制器處理 HTTP 請求一樣,訊息端點處理訊息。正如控制器對應到 URL 模式一樣,訊息端點對應到訊息通道。這兩種情況下的目標都是相同的:將應用程式程式碼與基礎架構隔離。這些概念以及隨後的所有模式在 企業整合模式 一書中都有詳細討論。在這裡,我們僅提供 Spring Integration 支援的主要端點類型以及與這些類型相關聯的角色的高階描述。後續章節將詳細說明並提供範例程式碼以及設定範例。
訊息轉換器
訊息轉換器負責轉換訊息的內容或結構,並傳回修改後的訊息。可能最常見的轉換器類型是將訊息的 Payload 從一種格式轉換為另一種格式的轉換器(例如從 XML 轉換為 java.lang.String
)。同樣地,轉換器可以新增、移除或修改訊息的標頭值。
訊息篩選器
訊息篩選器決定訊息是否應完全傳遞到輸出通道。這只需要一個布林測試方法,該方法可以檢查特定的 Payload 內容類型、屬性值、標頭的存在或其他條件。如果訊息被接受,它將被傳送到輸出通道。如果沒有,則會被丟棄(或者,對於更嚴格的實作,可能會拋出 Exception
)。訊息篩選器通常與發佈-訂閱通道結合使用,其中多個消費者可能會接收相同的訊息,並使用篩選器的條件來縮小要處理的訊息集。
請注意,不要將管道和篩選器架構模式中「篩選器」的通用用法與此特定端點類型混淆,後者有選擇地縮小在兩個通道之間流動的訊息。管道和篩選器架構「篩選器」的概念更接近 Spring Integration 的訊息端點:任何可以連接到訊息通道以傳送或接收訊息的元件。 |
訊息路由器
訊息路由器負責決定下一個應該接收訊息的通道(或多個通道)(如果有的話)。通常,決策基於訊息的內容或訊息標頭中可用的中繼資料。訊息路由器通常用作服務啟動器或其他能夠傳送回覆訊息的端點上靜態設定的輸出通道的動態替代方案。同樣地,訊息路由器為多個訂閱者使用的反應式訊息篩選器提供了主動的替代方案,如前所述。

分割器
分割器是另一種訊息端點類型,其職責是從其輸入通道接收訊息,將該訊息分割為多個訊息,並將每個訊息傳送到其輸出通道。這通常用於將「複合」Payload 物件劃分為一組包含細分 Payload 的訊息。
彙集器
基本上是分割器的鏡像,彙集器是一種訊息端點類型,它接收多個訊息並將它們組合為單個訊息。實際上,彙集器通常是包含分割器的管道中的下游消費者。從技術上講,彙集器比分割器更複雜,因為它需要維護狀態(要彙集的訊息)、決定何時可以使用完整的訊息組以及在必要時逾時。此外,在逾時的情況下,彙集器需要知道是傳送部分結果、丟棄它們還是將它們傳送到單獨的通道。Spring Integration 提供了 CorrelationStrategy
、ReleaseStrategy
以及用於逾時、是否在逾時時傳送部分結果以及丟棄通道的可設定設定。
服務啟動器
服務啟動器是用於將服務實例連接到訊息傳遞系統的通用端點。必須設定輸入訊息通道,並且如果要調用的服務方法能夠傳回值,則也可以提供輸出訊息通道。
輸出通道是可選的,因為每個訊息也可以提供自己的「回覆位址」標頭。此規則適用於所有消費者端點。 |
服務啟動器調用某些服務物件上的操作以處理請求訊息,提取請求訊息的 Payload 並進行轉換(如果該方法不期望訊息類型參數)。每當服務物件的方法傳回值時,如果需要(如果它還不是訊息類型),該傳回值也會轉換為回覆訊息。該回覆訊息會傳送到輸出通道。如果未設定輸出通道,則回覆會傳送到訊息的「回覆位址」中指定的通道(如果可用)。
請求-回覆服務啟動器端點將目標物件的方法連接到輸入和輸出訊息通道。

如先前在 訊息通道 中所討論的,通道可以是可輪詢的或可訂閱的。在前面的圖表中,這以「時鐘」符號和實線箭頭(輪詢)以及虛線箭頭(訂閱)表示。 |
通道配接器
通道配接器是一種端點,它將訊息通道連接到其他系統或傳輸方式。通道配接器可以是入埠或出埠。通常,通道配接器會在訊息與從其他系統(檔案、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
中。此包裝器支援請求處理器建議處理,並發出正常的 'produced no reply' 偵錯日誌訊息。其 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),這些 bean 用於支援執行階段環境。
4.0 版引入了 @EnableIntegration 註解,以允許註冊 Spring Integration 基礎結構 bean(請參閱 Javadoc)。當僅使用 Java 設定時,才需要此註解 — 例如,使用 Spring Boot 或 Spring Integration Messaging Annotation 支援以及沒有 XML 整合設定的 Spring Integration Java DSL。 |
當您有一個沒有 Spring Integration 元件的父層上下文和兩個或多個使用 Spring Integration 的子層上下文時,@EnableIntegration
註解也很有用。它允許這些通用元件僅在父層上下文中宣告一次。
@EnableIntegration
註解會向應用程式上下文註冊許多基礎結構元件。特別是,它
-
註冊一些內建 bean,例如
errorChannel
及其LoggingHandler
、用於輪詢器的taskScheduler
、jsonPath
SpEL 函數等。 -
新增多個
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
方法上。
有關訊息傳遞註解的更多資訊,請參閱 註解支援。
程式設計考量
您應盡可能使用純舊 Java 物件 (POJO),並且僅在絕對必要時才在程式碼中公開框架。有關更多資訊,請參閱 POJO 方法調用。
如果您確實將框架公開給您的類別,則需要考慮一些事項,尤其是在應用程式啟動期間
-
如果您的元件是
ApplicationContextAware
,您通常不應在setApplicationContext()
方法中使用ApplicationContext
。而是儲存參考並延遲此類用法,直到上下文生命週期的稍後階段。 -
如果您的元件是
InitializingBean
或使用@PostConstruct
方法,請勿從這些初始化方法傳送任何訊息。在調用這些方法時,應用程式上下文尚未初始化,並且傳送此類訊息很可能會失敗。如果您需要在啟動期間傳送訊息,請實作ApplicationListener
並等待ContextRefreshedEvent
。或者,實作SmartLifecycle
,將您的 bean 放在較晚的階段,並從start()
方法傳送訊息。
使用封裝(例如,Shaded)Jar 時的考量
Spring Integration 透過使用 Spring Framework 的 SpringFactories
機制載入多個 IntegrationConfigurationInitializer
類別來啟動某些功能。這包括 -core
jar 以及某些其他 jar,包括 -http
和 -jmx
。此過程的資訊儲存在每個 jar 中的 META-INF/spring.factories
檔案中。
一些開發人員更喜歡使用眾所周知的工具(例如 Apache Maven Shade Plugin)將其應用程式和所有依賴項重新封裝到單個 jar 中。
預設情況下,shade 外掛程式在產生 shaded jar 時不會合併 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 |
受影響的模組為
-
core
(spring-integration.xsd
) -
file
-
http
-
jms
-
mail
-
security
-
stream
-
ws
-
xml
尋找 Java 和 DSL 設定的類別名稱
透過 XML 設定和 Spring Integration 命名空間支援,XML 剖析器會隱藏目標 bean 的宣告方式和連線方式。對於 Java 設定,重要的是要了解目標終端使用者應用程式的 Framework API。
EIP 實作的第一類公民是 Message
、Channel
和 Endpoint
(請參閱本章稍早的 主要元件)。它們的實作(合約)是
前兩個足夠簡單,可以理解如何實作、設定和使用。最後一個值得更多關注
AbstractEndpoint
在整個 Spring Framework 中廣泛用於不同的元件實作。其主要實作是
-
EventDrivenConsumer
,當我們訂閱SubscribableChannel
以監聽訊息時使用。 -
PollingConsumer
,當我們從PollableChannel
輪詢訊息時使用。
當您使用訊息傳遞註解或 Java DSL 時,您無需擔心這些元件,因為 Framework 會使用適當的註解和 BeanPostProcessor
實作自動產生它們。當手動建置元件時,您應使用 ConsumerEndpointFactoryBean
來協助判斷要建立的目標 AbstractEndpoint
消費者實作,這取決於提供的 inputChannel
屬性。
另一方面,ConsumerEndpointFactoryBean
會委派給 Framework 中的另一個第一類公民 - org.springframework.messaging.MessageHandler
。此介面實作的目標是處理端點從通道取用的訊息。Spring Integration 中的所有 EIP 元件都是 MessageHandler
實作(例如,AggregatingMessageHandler
、MessageTransformingHandler
、AbstractMessageSplitter
等)。目標協定出埠配接器(FileWritingMessageHandler
、HttpRequestExecutingMessageHandler
、AbstractMqttMessageHandler
等)也是 MessageHandler
實作。當您使用 Java 設定開發 Spring Integration 應用程式時,您應查看 Spring Integration 模組以尋找適合用於 @ServiceActivator
設定的 MessageHandler
實作。例如,若要傳送 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,您不應擔心此類別。Framework 會根據 @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
以監聽 socket 上的框架並產生到通道的訊息。 -
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 編譯。