使用 Spring JMS

本節說明如何使用 Spring 的 JMS 組件。

使用 JmsTemplate

JmsTemplate 類別是 JMS 核心套件中的核心類別。它簡化了 JMS 的使用,因為它在傳送或同步接收訊息時處理資源的建立和釋放。

使用 JmsTemplate 的程式碼只需要實作回呼介面,這些介面為它們提供明確定義的高階合約。MessageCreator 回呼介面會在給定呼叫程式碼在 JmsTemplate 中提供的 Session 時建立訊息。為了允許更複雜的 JMS API 用法,SessionCallback 提供 JMS 工作階段,而 ProducerCallback 公開 SessionMessageProducer 配對。

JMS API 公開了兩種傳送方法,一種將傳遞模式、優先順序和存活時間作為服務品質 (QOS) 參數,另一種則不採用 QOS 參數並使用預設值。由於 JmsTemplate 有許多傳送方法,因此已將 QOS 參數設定為 Bean 屬性,以避免傳送方法數量重複。同樣地,同步接收呼叫的逾時值是透過使用 setReceiveTimeout 屬性來設定。

某些 JMS 提供者允許透過 ConnectionFactory 的配置,以管理方式設定預設 QOS 值。這會導致呼叫 MessageProducer 實例的 send 方法 (send(Destination destination, Message message)) 使用與 JMS 規範中指定的預設 QOS 值不同的值。為了提供 QOS 值的一致管理,因此必須明確啟用 JmsTemplate 以使用其自己的 QOS 值,方法是將布林屬性 isExplicitQosEnabled 設定為 true

為了方便起見,JmsTemplate 也公開了基本請求-回覆操作,允許傳送訊息並等待在作為操作一部分建立的暫時佇列上回覆。

JmsTemplate 類別的實例在配置後是執行緒安全的。這很重要,因為這表示您可以配置 JmsTemplate 的單一實例,然後安全地將此共用參考注入到多個協作者中。為了清楚起見,JmsTemplate 是具狀態的,因為它維護對 ConnectionFactory 的參考,但此狀態不是交談狀態。

從 Spring Framework 4.1 開始,JmsMessagingTemplate 建置在 JmsTemplate 之上,並提供與訊息傳遞抽象的整合 — 也就是 org.springframework.messaging.Message。這可讓您以一般方式建立要傳送的訊息。

連線

JmsTemplate 需要參考 ConnectionFactoryConnectionFactory 是 JMS 規範的一部分,並作為使用 JMS 的進入點。用戶端應用程式會將其用作工廠,以建立與 JMS 提供者的連線,並封裝各種配置參數,其中許多參數是廠商特定的,例如 SSL 配置選項。

在 EJB 內使用 JMS 時,廠商會提供 JMS 介面的實作,以便它們可以參與宣告式交易管理,並執行連線和工作階段的集區。為了在 EJB 內使用 JmsTemplate 的這些功能,Jakarta EE 容器通常要求您在 EJB 或 Servlet 部署描述器內宣告 JMS 連線工廠作為 resource-ref。為了確保在 EJB 內使用 JmsTemplate 的這些功能,用戶端應用程式應確保它參考 ConnectionFactory 的受管理實作。

快取訊息傳遞資源

標準 API 涉及建立許多中繼物件。若要傳送訊息,會執行下列「API」逐步執行

ConnectionFactory->Connection->Session->MessageProducer->send

ConnectionFactorySend 操作之間,會建立和銷毀三個中繼物件。為了最佳化資源使用量並提高效能,Spring 提供了兩種 ConnectionFactory 實作。

使用 SingleConnectionFactory

Spring 提供了 ConnectionFactory 介面的實作 SingleConnectionFactory,它會在所有 createConnection() 呼叫中傳回相同的 Connection,並忽略對 close() 的呼叫。這對於測試和獨立環境很有用,如此一來,相同的連線可用於可能跨越任意數目交易的多個 JmsTemplate 呼叫。SingleConnectionFactory 採用對標準 ConnectionFactory 的參考,此參考通常來自 JNDI。

使用 CachingConnectionFactory

CachingConnectionFactory 擴充了 SingleConnectionFactory 的功能,並新增了 SessionMessageProducerMessageConsumer 實例的快取。初始快取大小設定為 1。您可以使用 sessionCacheSize 屬性來增加快取工作階段的數目。請注意,實際快取工作階段的數目大於該數目,因為工作階段是根據其確認模式快取的,因此當 sessionCacheSize 設定為一時,最多可能會有四個快取工作階段實例 (每個確認模式一個)。MessageProducerMessageConsumer 實例會在其擁有的工作階段內快取,並且在快取時也會考量生產者和消費者的唯一屬性。MessageProducer 會根據其目的地快取。MessageConsumer 會根據由目的地、選取器、noLocal 傳遞旗標和持久訂閱名稱 (如果建立持久消費者) 組成的金鑰快取。

暫時佇列和主題 (TemporaryQueue/TemporaryTopic) 的 MessageProducer 和 MessageConsumer 永遠不會快取。不幸的是,WebLogic JMS 碰巧在其一般目的地實作上實作暫時佇列/主題介面,錯誤地指出其目的地都無法快取。請在 WebLogic 上使用不同的連線集區/快取,或針對 WebLogic 用途自訂 CachingConnectionFactory

目的地管理

目的地與 ConnectionFactory 實例一樣,是您可以在 JNDI 中儲存和擷取的 JMS 管理物件。在配置 Spring 應用程式內容時,您可以使用 JNDI JndiObjectFactoryBean 工廠類別或 <jee:jndi-lookup> 對物件對 JMS 目的地的參考執行依賴注入。但是,如果應用程式中有大量目的地,或 JMS 提供者有目的地管理進階功能,則此策略通常很麻煩。此類進階目的地管理的範例包括建立動態目的地或支援目的地的階層式命名空間。JmsTemplate 會將目的地名稱的解析委派給實作 DestinationResolver 介面的 JMS 目的地物件。DynamicDestinationResolverJmsTemplate 使用的預設實作,可容納解析動態目的地。也提供 JndiDestinationResolver 作為 JNDI 中包含的目的地的服務定位器,並選擇性地回復為 DynamicDestinationResolver 中包含的行為。

在 JMS 應用程式中使用的目的地通常僅在執行階段才知道,因此無法在部署應用程式時以管理方式建立。這通常是因為互動系統組件之間有共用的應用程式邏輯,這些組件會根據眾所周知的命名慣例在執行階段建立目的地。即使動態目的地的建立不是 JMS 規範的一部分,但大多數廠商都提供了此功能。動態目的地是使用使用者定義的名稱建立的,這會將它們與暫時目的地區分開來,而且通常不會在 JNDI 中註冊。用於建立動態目的地的 API 因提供者而異,因為與目的地相關聯的屬性是廠商特定的。但是,廠商有時會做出一個簡單的實作選擇,即忽略 JMS 規範中的警告,並使用方法 TopicSession createTopic(String topicName)QueueSession createQueue(String queueName) 方法來建立具有預設目的地屬性的新目的地。根據廠商實作,DynamicDestinationResolver 接著也可以建立實體目的地,而不僅僅是解析一個。

布林屬性 pubSubDomain 用於配置 JmsTemplate,使其瞭解正在使用哪個 JMS 網域。根據預設,此屬性的值為 false,表示將使用點對點網域 Queues。此屬性 (由 JmsTemplate 使用) 透過 DestinationResolver 介面的實作來決定動態目的地解析的行為。

您也可以透過屬性 defaultDestination 使用預設目的地配置 JmsTemplate。預設目的地適用於未參考特定目的地的傳送和接收操作。

訊息接聽器容器

在 EJB 世界中,JMS 訊息最常見的用途之一是驅動訊息驅動 Bean (MDB)。Spring 提供了一種建立訊息驅動 POJO (MDP) 的解決方案,這種方式不會將使用者繫結到 EJB 容器。(如需 Spring MDP 支援的詳細涵蓋範圍,請參閱 非同步接收:訊息驅動 POJO。) 從 Spring Framework 4.1 開始,端點方法可以使用 @JmsListener 註解 — 如需更多詳細資訊,請參閱 註解驅動的接聽器端點

訊息接聽器容器用於從 JMS 訊息佇列接收訊息,並驅動注入其中的 MessageListener。接聽器容器負責訊息接收的所有執行緒處理,並分派到接聽器以進行處理。訊息接聽器容器是 MDP 和訊息傳遞提供者之間的中介,並負責註冊以接收訊息、參與交易、資源取得和釋放、例外轉換等等。這可讓您撰寫與接收訊息相關聯的 (可能很複雜的) 業務邏輯 (並可能回應訊息),並將樣板 JMS 基礎結構問題委派給框架。

Spring 封裝了兩個標準 JMS 訊息接聽器容器,每個容器都有其專門的功能集。

使用 SimpleMessageListenerContainer

此訊息接聽器容器是兩種標準類型中較簡單的一種。它會在啟動時建立固定數目的 JMS 工作階段和消費者,使用標準 JMS MessageConsumer.setMessageListener() 方法註冊接聽器,並將接聽器回呼的執行權交給 JMS 提供者。此變體不允許動態適應執行階段需求,也不允許參與外部管理交易。在相容性方面,它非常接近獨立 JMS 規範的精神,但通常與 Jakarta EE 的 JMS 限制不相容。

雖然 SimpleMessageListenerContainer 不允許參與外部管理交易,但它確實支援原生 JMS 交易。若要啟用此功能,您可以將 sessionTransacted 旗標切換為 true,或在 XML 命名空間中,將 acknowledge 屬性設定為 transacted。然後,從接聽器擲回的例外會導致回滾,訊息會重新傳遞。或者,考慮使用 CLIENT_ACKNOWLEDGE 模式,該模式也會在發生例外時提供重新傳遞,但不使用交易式 Session 實例,因此不包括交易協定中的任何其他 Session 操作 (例如傳送回應訊息)。
預設 AUTO_ACKNOWLEDGE 模式不提供適當的可靠性保證。當接聽器執行失敗時 (因為提供者會在叫用接聽器後自動確認每個訊息,而不會將任何例外傳播到提供者) 或當接聽器容器關閉時 (您可以透過設定 acceptMessagesWhileStopping 旗標來配置此行為),訊息可能會遺失。請務必在需要可靠性的情況下使用交易式工作階段 (例如,用於可靠的佇列處理和持久主題訂閱)。

使用 DefaultMessageListenerContainer

此訊息接聽器容器在大多數情況下使用。與 SimpleMessageListenerContainer 相反,此容器變體允許動態適應執行階段需求,並且能夠參與外部管理交易。每個接收到的訊息都會在使用 JtaTransactionManager 配置時註冊 XA 交易。因此,處理可能會利用 XA 交易語意。此接聽器容器在 JMS 提供者的低需求、進階功能 (例如參與外部管理交易) 和與 Jakarta EE 環境的相容性之間取得了良好的平衡。

您可以自訂容器的快取層級。請注意,在未啟用快取時,每次接收訊息都會建立新的連線和新的工作階段。將此與高負載的非持久訂閱結合可能會導致訊息遺失。在這種情況下,請務必使用適當的快取層級。

當 Broker 關閉時,此容器也具有可復原功能。根據預設,簡單的 BackOff 實作每五秒重試一次。您可以為更精細的復原選項指定自訂 BackOff 實作。如需範例,請參閱 ExponentialBackOff

與其同級 (SimpleMessageListenerContainer) 一樣,DefaultMessageListenerContainer 支援原生 JMS 交易,並允許自訂確認模式。如果您的案例可行,強烈建議使用外部管理交易 — 也就是說,如果您可以接受 JVM 關閉時偶爾出現重複訊息的情況。您業務邏輯中的自訂重複訊息偵測步驟可以涵蓋這種情況 — 例如,以業務實體存在性檢查或協定表檢查的形式。任何此類安排都比替代方案有效率得多:使用 XA 交易 (透過使用 JtaTransactionManager 配置 DefaultMessageListenerContainer) 包裝整個處理程序,以涵蓋 JMS 訊息的接收以及訊息接聽器中業務邏輯的執行 (包括資料庫操作等)。
預設 AUTO_ACKNOWLEDGE 模式不提供適當的可靠性保證。當接聽器執行失敗時 (因為提供者會在叫用接聽器後自動確認每個訊息,而不會將任何例外傳播到提供者) 或當接聽器容器關閉時 (您可以透過設定 acceptMessagesWhileStopping 旗標來配置此行為),訊息可能會遺失。請務必在需要可靠性的情況下使用交易式工作階段 (例如,用於可靠的佇列處理和持久主題訂閱)。

交易管理

Spring 提供 JmsTransactionManager,用於管理單一 JMS ConnectionFactory 的交易。這可讓 JMS 應用程式利用 Spring 的受管理交易功能,如資料存取章節的 交易管理章節 中所述。JmsTransactionManager 執行本機資源交易,將來自指定 ConnectionFactory 的 JMS 連線/工作階段配對繫結到執行緒。JmsTemplate 會自動偵測此類交易資源,並據此操作。

在 Jakarta EE 環境中,ConnectionFactory 會集區 ConnectionSession 實例,因此這些資源可以在交易中有效率地重複使用。在獨立環境中,使用 Spring 的 SingleConnectionFactory 會產生共用的 JMS Connection,每個交易都有其自己的獨立 Session。或者,考慮使用提供者特定的集區配接器,例如 ActiveMQ 的 PooledConnectionFactory 類別。

您也可以搭配 JtaTransactionManager 和支援 XA 的 JMS ConnectionFactory 使用 JmsTemplate,以執行分散式交易。請注意,這需要使用 JTA 交易管理員以及正確設定 XA 的 ConnectionFactory。(請查閱您的 Jakarta EE 伺服器或 JMS 提供者的文件。)

當使用 JMS API 從 Connection 建立 Session 時,在受管理和非受管理的交易環境中重複使用程式碼可能會令人困惑。這是因為 JMS API 只有一個工廠方法可以建立 Session,而且它需要交易和確認模式的值。在受管理的環境中,設定這些值是環境交易基礎架構的責任,因此這些值會被供應商 JMS 連線的包裝器忽略。當您在非受管理的環境中使用 JmsTemplate 時,您可以透過使用 sessionTransactedsessionAcknowledgeMode 屬性來指定這些值。當您將 PlatformTransactionManagerJmsTemplate 一起使用時,範本始終會獲得一個交易式 JMS Session