Spring JMS の使用

このセクションでは、Spring の JMS コンポーネントの使用方法について説明します。

JmsTemplate を使用する

JmsTemplate クラスは、JMS コアパッケージの中心的なクラスです。メッセージの送信または同期受信時にリソースの作成と解放を処理するため、JMS の使用が簡単になります。

JmsTemplate を使用するコードは、明確に定義された高レベルの契約を提供するコールバックインターフェースを実装するだけで済みます。MessageCreator コールバックインターフェースは、JmsTemplate の呼び出しコードによって提供される Session が与えられると、メッセージを作成します。JMS API のより複雑な使用を可能にするために、SessionCallback は JMS セッションを提供し、ProducerCallback は Session と MessageProducer のペアを公開します。

JMS API は、2 つの型の送信メソッドを公開します。1 つは配信モード、優先度、有効期間を Quality of Service(QOS)パラメーターとして使用する方法、もう 1 つは QOS パラメーターを使用せずデフォルト値を使用する方法です。JmsTemplate には多くの送信メソッドがあるため、QOS パラメーターの設定は Bean プロパティとして公開されており、送信メソッドの数の重複を避けています。同様に、同期受信呼び出しのタイムアウト値は、setReceiveTimeout プロパティを使用して設定されます。

一部の JMS プロバイダーでは、ConnectionFactory の構成を介して管理上のデフォルトの QOS 値を設定できます。これには、MessageProducer インスタンスの send メソッド(send(Destination destination, Message message))の呼び出しが、JMS 仕様で指定されているものとは異なる QOS デフォルト値を使用するという効果があります。QOS 値の一貫した管理を実現するには、ブールプロパティ isExplicitQosEnabled を true に設定して、JmsTemplate が独自の QOS 値を使用できるように明確に有効にする必要があります。

便宜上、JmsTemplate は、操作の一部として作成された一時キューでメッセージの送信と応答の待機を可能にする基本的なリクエスト / 応答操作も公開します。

JmsTemplate クラスのインスタンスは、一度設定されるとスレッドセーフです。これは重要です。なぜなら、JmsTemplate の単一のインスタンスを構成してから、この共有参照を複数のコラボレーターに安全に注入できるからです。明確にするために、JmsTemplate はステートフルであり、ConnectionFactory への参照を維持しますが、この状態は会話状態ではありません。

Spring Framework 4.1 以降、JmsMessagingTemplate は JmsTemplate 上に構築され、メッセージングの抽象化、つまり org.springframework.messaging.Message との統合を提供します。これにより、一般的な方法で送信するメッセージを作成できます。

接続

JmsTemplate には、ConnectionFactory への参照が必要です。ConnectionFactory は JMS 仕様の一部であり、JMS を操作するためのエントリポイントとして機能します。クライアントアプリケーションがファクトリとして使用して JMS プロバイダーとの接続を作成し、SSL 構成オプションなど、ベンダー固有のさまざまな構成パラメーターをカプセル化します。

EJB 内で JMS を使用する場合、ベンダーは、宣言的なトランザクション管理に参加し、接続とセッションのプーリングを実行できるように、JMS インターフェースの実装を提供します。この実装を使用するために、Jakarta EE コンテナーでは通常、EJB またはサーブレットデプロイ記述子内で JMS 接続ファクトリを resource-ref として宣言する必要があります。EJB 内の JmsTemplate でこれらの機能を確実に使用するには、クライアントアプリケーションが ConnectionFactory の管理された実装を参照することを確認する必要があります。

メッセージングリソースのキャッシュ

標準 API には、多くの中間オブジェクトの作成が含まれます。メッセージを送信するには、次の "API" ウォークが実行されます。

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

ConnectionFactory 操作と Send 操作の間で、3 つの中間オブジェクトが作成および破棄されます。リソースの使用を最適化し、パフォーマンスを向上させるために、Spring は ConnectionFactory の 2 つの実装を提供します。

SingleConnectionFactory を使用する

Spring は、すべての createConnection() 呼び出しで同じ Connection を返し、close() への呼び出しを無視する ConnectionFactory インターフェース SingleConnectionFactory の実装を提供します。これは、テスト環境およびスタンドアロン環境で役立ちます。そのため、同じ接続を任意の数のトランザクションにまたがる複数の JmsTemplate 呼び出しに使用できます。SingleConnectionFactory は、通常 JNDI から提供される標準 ConnectionFactory への参照を取ります。

CachingConnectionFactory を使用する

CachingConnectionFactory は、SingleConnectionFactory の機能を継承し、SessionMessageProducerMessageConsumer インスタンスのキャッシュを追加します。初期キャッシュサイズは 1 に設定されています。sessionCacheSize プロパティを使用して、キャッシュされるセッションの数を増やすことができます。セッションは確認応答モードに基づいてキャッシュされるため、実際にキャッシュされるセッションの数はその数よりも多いことに注意してください。sessionCacheSize が 1 に設定されている場合、最大 4 つのキャッシュセッションインスタンス(確認応答モードごとに 1 つ)が存在する可能性があります。MessageProducer および MessageConsumer インスタンスは、所有するセッション内でキャッシュされ、キャッシュ時にプロデューサーとコンシューマーの固有のプロパティも考慮に入れます。MessageProducers は、宛先に基づいてキャッシュされます。MessageConsumers は、宛先、セレクター、noLocal 配信フラグ、永続サブスクリプション名(永続コンシューマーを作成する場合)で構成されるキーに基づいてキャッシュされます。

一時キューおよびトピックの MessageProducers および MessageConsumers(TemporaryQueue/TemporaryTopic)はキャッシュされません。残念ながら、WebLogic JMS は、通常の宛先実装に一時キュー / トピックインターフェースを実装しているため、宛先をキャッシュできないことを誤って示しています。WebLogic で別の接続プール / キャッシュを使用するか、WebLogic の目的で CachingConnectionFactory をカスタマイズしてください。

宛先管理

宛先は、ConnectionFactory インスタンスとして、JNDI に格納および取得できる JMS 管理対象オブジェクトです。Spring アプリケーションコンテキストを設定する場合、JNDI JndiObjectFactoryBean ファクトリクラスまたは <jee:jndi-lookup> を使用して、JMS 宛先へのオブジェクトの参照に対して依存性注入を実行できます。ただし、アプリケーションに多数の宛先がある場合、JMS プロバイダーに固有の高度な宛先管理機能がある場合、この戦略はしばしば面倒です。このような高度な宛先管理の例には、動的な宛先の作成や、宛先の階層的な名前空間のサポートが含まれます。JmsTemplate は、宛先名の解決を DestinationResolver インターフェースを実装する JMS 宛先オブジェクトに委譲します。DynamicDestinationResolver は、JmsTemplate で使用されるデフォルトの実装であり、動的な宛先の解決に対応します。JndiDestinationResolver は、JNDI に含まれる宛先のサービスロケーターとして機能するようにも提供されており、オプションで DynamicDestinationResolver に含まれる動作にフォールバックします。

多くの場合、JMS アプリケーションで使用される宛先は実行時にのみ認識されるため、アプリケーションのデプロイ時に管理上作成することはできません。これは多くの場合、よく知られている命名規則に従って実行時に宛先を作成する相互作用するシステムコンポーネント間に共有アプリケーションロジックがあるためです。動的宛先の作成は JMS 仕様の一部ではありませんが、ほとんどのベンダーがこの機能を提供しています。動的宛先は、一時宛先と区別するためのユーザー定義名で作成され、多くの場合、JNDI に登録されません。宛先に関連付けられたプロパティはベンダー固有であるため、動的な宛先の作成に使用される API はプロバイダーごとに異なります。ただし、ベンダーによって行われることがある単純な実装の選択は、JMS 仕様の警告を無視し、メソッド TopicSessioncreateTopic(String topicName) または QueueSessioncreateQueue(String queueName) メソッドを使用して、デフォルトの宛先プロパティを持つ新しい宛先を作成することです。ベンダーの実装に応じて、DynamicDestinationResolver は、1 つだけを解決するのではなく、物理的な宛先を作成することもできます。

ブールプロパティ pubSubDomain は、使用されている JMS ドメインの知識で JmsTemplate を構成するために使用されます。デフォルトでは、このプロパティの値は false であり、ポイントツーポイントドメイン Queues が使用されることを示します。このプロパティ(JmsTemplate で使用)は、DestinationResolver インターフェースの実装による動的な宛先解決の動作を決定します。

プロパティ defaultDestination を使用して、デフォルトの宛先で JmsTemplate を構成することもできます。デフォルトの宛先は、特定の宛先を参照しない送信および受信操作です。

メッセージリスナコンテナー

EJB における JMS メッセージの最も一般的な用途の 1 つは、メッセージ駆動型 Bean(MDB)を駆動することです。Spring は、ユーザーを EJB コンテナーに関連付けない方法でメッセージ駆動型 POJO(MDP)を作成するソリューションを提供します。(Spring の MDP サポートの詳細については、非同期受信: メッセージ駆動型 POJO を参照してください)Spring Framework 4.1 以降、エンドポイントメソッドには @JmsListener でアノテーションを付けることができます。詳細については、アノテーション駆動型のリスナーエンドポイントを参照してください。

メッセージリスナコンテナーは、JMS メッセージキューからメッセージを受信し、そこに挿入される MessageListener を駆動するために使用されます。リスナーコンテナーは、メッセージ受信のすべてのスレッド化を処理し、処理のためにリスナーにディスパッチします。メッセージリスナコンテナーは、MDP とメッセージングプロバイダ間の仲介者であり、メッセージの受信登録、トランザクションへの参加、リソースの取得と解放、例外変換などを処理します。これにより、メッセージの受信(および場合によっては応答)に関連する(場合によっては複雑な)ビジネスロジックを記述でき、JMS インフラストラクチャに関する定型的な問題をフレームワークに委譲できます。

Spring にパッケージ化された 2 つの標準 JMS メッセージリスナーコンテナーがあり、それぞれに専用の機能セットがあります。

SimpleMessageListenerContainer を使用する

このメッセージリスナコンテナーは、2 つの標準フレーバのうち単純なものです。起動時に一定数の JMS セッションとコンシューマーを作成し、標準の JMS MessageConsumer.setMessageListener() メソッドを使用してリスナーを登録し、リスナーコールバックを実行するために JMS プロバイダーに任せます。このバリアントでは、ランタイムの要求に動的に適応したり、外部管理トランザクションに参加したりすることはできません。互換性に関しては、スタンドアロン JMS 仕様の精神に非常に近いままですが、一般的に Jakarta EE の JMS 制限と互換性がありません。

SimpleMessageListenerContainer は外部管理トランザクションへの参加を許可しませんが、ネイティブ JMS トランザクションをサポートします。この機能を有効にするには、sessionTransacted フラグを true に切り替えるか、XML 名前空間で acknowledge 属性を transacted に設定します。リスナーからスローされた例外はロールバックにつながり、メッセージが再配信されます。または、CLIENT_ACKNOWLEDGE モードの使用を検討してください。CLIENT_ACKNOWLEDGE モードは、例外の場合にも再配信を提供しますが、トランザクション Session インスタンスを使用しないため、トランザクションプロトコルに他の Session 操作(レスポンスメッセージの送信など)は含まれません。
デフォルトの AUTO_ACKNOWLEDGE モードは、適切な信頼性の保証を提供しません。リスナーの実行が失敗すると(プロバイダーがリスナー呼び出し後に各メッセージを自動的に確認するため、プロバイダーに例外が伝播されないため)、リスナーコンテナーがシャットダウンすると(acceptMessagesWhileStopping フラグを設定することでこれを構成できます)メッセージは失われます。信頼性が必要な場合は、トランザクションセッションを必ず使用してください(たとえば、信頼性の高いキュー処理や永続的なトピックサブスクリプションなど)。

DefaultMessageListenerContainer を使用する

ほとんどの場合、このメッセージリスナコンテナーが使用されます。SimpleMessageListenerContainer とは対照的に、このコンテナーバリアントは、実行時の要求に動的に適応させることができ、外部で管理されたトランザクションに参加できます。JtaTransactionManager で構成された場合、受信した各メッセージは XA トランザクションに登録されます。その結果、処理は XA トランザクションのセマンティクスを利用できます。このリスナーコンテナーは、JMS プロバイダーの低い要件、高度な機能(外部管理トランザクションへの参加など)、および Jakarta EE 環境との互換性のバランスが取れています。

コンテナーのキャッシュレベルをカスタマイズできます。キャッシュが有効になっていない場合、メッセージの受信ごとに新しい接続と新しいセッションが作成されることに注意してください。これを高負荷の非永続サブスクリプションと組み合わせると、メッセージが失われる可能性があります。そのような場合は、適切なキャッシュレベルを使用してください。

このコンテナーには、ブローカーがダウンしたときに回復可能な機能もあります。デフォルトでは、単純な BackOff 実装は 5 秒ごとに再試行します。よりきめ細かいリカバリオプションのために、カスタム BackOff 実装を指定できます。例については、ExponentialBackOff (Javadoc) を参照してください。

その兄弟(SimpleMessageListenerContainer)と同様に、DefaultMessageListenerContainer はネイティブ JMS トランザクションをサポートし、確認応答モードをカスタマイズできます。シナリオで実行可能な場合、これは外部で管理されるトランザクションよりも強くお勧めします。つまり、JVM が停止した場合にメッセージが重複することがある場合です。ビジネスロジックのカスタム重複メッセージ検出手順は、このような状況をカバーできます。たとえば、ビジネスエンティティの存在チェックやプロトコルテーブルチェックの形式です。このような配置は、他の方法よりもはるかに効率的です。処理全体を XA トランザクションでラップして(DefaultMessageListenerContainer を JtaTransactionManager で構成することにより)、JMS メッセージの受信とメッセージリスナーでのビジネスロジックの実行をカバーします(データベース操作などを含む)。
デフォルトの AUTO_ACKNOWLEDGE モードは、適切な信頼性の保証を提供しません。リスナーの実行が失敗すると(プロバイダーがリスナー呼び出し後に各メッセージを自動的に確認するため、プロバイダーに例外が伝播されないため)、リスナーコンテナーがシャットダウンすると(acceptMessagesWhileStopping フラグを設定することでこれを構成できます)メッセージは失われます。信頼性が必要な場合は、トランザクションセッションを必ず使用してください(たとえば、信頼性の高いキュー処理や永続的なトピックサブスクリプションなど)。

トランザクション管理

Spring は、単一の JMS ConnectionFactory のトランザクションを管理する JmsTransactionManager を提供します。これにより、データアクセスの章のトランザクション管理セクションに従って、JMS アプリケーションは Spring のマネージドトランザクション機能を活用できます。JmsTransactionManager はローカルリソーストランザクションを実行し、指定された ConnectionFactory からの JMS 接続 / セッションペアをスレッドにバインドします。JmsTemplate は、このようなトランザクションリソースを自動的に検出し、それに応じて動作します。

Jakarta EE 環境では、ConnectionFactory は接続およびセッションインスタンスをプールするため、これらのリソースはトランザクション全体で効率的に再利用されます。スタンドアロン環境では、Spring の SingleConnectionFactory を使用すると、各 JMS Connection が共有され、各トランザクションは独自の独立した Session を持ちます。または、ActiveMQ の PooledConnectionFactory クラスなど、プロバイダー固有のプーリングアダプターの使用を検討してください。

JmsTemplate を JtaTransactionManager および XA 対応 JMS ConnectionFactory とともに使用して、分散トランザクションを実行することもできます。これには、JTA トランザクションマネージャーと、適切に XA が構成された ConnectionFactory を使用する必要があることに注意してください。(Jakarta EE サーバーまたは JMS プロバイダーのドキュメントを確認してください。)

JMS API を使用して Connection から Session を作成する場合、管理されたトランザクション環境と管理されていないトランザクション環境でコードを再利用すると混乱する可能性があります。これは、JMS API には Session を作成するファクトリメソッドが 1 つしかなく、トランザクションモードと確認応答モードの値が必要です。管理された環境では、これらの値の設定は環境のトランザクションインフラストラクチャの責任であるため、これらの値はベンダーの JMS 接続へのラッパーによって無視されます。管理されていない環境で JmsTemplate を使用する場合、プロパティ sessionTransacted および sessionAcknowledgeMode を使用してこれらの値を指定できます。JmsTemplate で PlatformTransactionManager を使用する場合、テンプレートには常にトランザクション JMS JMS Session が与えられます。