アノテーション駆動型のリスナーエンドポイント
メッセージを非同期で受信する最も簡単な方法は、アノテーション付きのリスナーエンドポイントインフラストラクチャを使用することです。簡単に言えば、管理対象の Bean のメソッドを JMS リスナーエンドポイントとして公開できます。次の例は、その使用方法を示しています。
@Component
public class MyService {
@JmsListener(destination = "myDestination")
public void processOrder(String data) { ... }
}
前の例の考え方は、メッセージが jakarta.jms.Destination
myDestination
で使用可能であるときはいつでも、それに応じて processOrder
メソッドが呼び出されるということです(この場合、JMS メッセージの内容で、MessageListenerAdapter
が提供するものと同様です)。
アノテーション付きエンドポイントインフラストラクチャは、JmsListenerContainerFactory
を使用して、アノテーション付きメソッドごとに背後でメッセージリスナーコンテナーを作成します。このようなコンテナーは、アプリケーションコンテキストに対して登録されていませんが、JmsListenerEndpointRegistry
Bean を使用して管理目的で簡単に見つけることができます。
@JmsListener は Java 8 の繰り返し可能なアノテーションであるため、@JmsListener 宣言を追加することにより、複数の JMS 宛先を同じメソッドに関連付けることができます。 |
リスナーエンドポイントアノテーションを有効にする
@JmsListener
アノテーションのサポートを有効にするには、次の例に示すように、@EnableJms
を @Configuration
クラスの 1 つに追加できます。
@Configuration
@EnableJms
public class AppConfig {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setDestinationResolver(destinationResolver());
factory.setSessionTransacted(true);
factory.setConcurrency("3-10");
return factory;
}
}
デフォルトでは、インフラストラクチャは、メッセージリスナーコンテナーを作成するために使用するファクトリのソースとして、jmsListenerContainerFactory
という名前の Bean を探します。この場合 (JMS インフラストラクチャの設定を無視して)、コアプールサイズを 3 スレッド、最大プールサイズを 10 スレッドにして、processOrder
メソッドを呼び出すことができます。
リスナーコンテナーファクトリをカスタマイズして各アノテーションに使用するか、JmsListenerConfigurer
インターフェースを実装して明示的なデフォルトを設定できます。デフォルトは、特定のコンテナーファクトリなしで少なくとも 1 つのエンドポイントが登録されている場合にのみ必要です。詳細と例については、JmsListenerConfigurer
(Javadoc) を実装するクラスの javadoc を参照してください。
XML 構成を好む場合、次の例に示すように、<jms:annotation-driven>
要素を使用できます。
<jms:annotation-driven/>
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationResolver" ref="destinationResolver"/>
<property name="sessionTransacted" value="true"/>
<property name="concurrency" value="3-10"/>
</bean>
プログラムによるエンドポイント登録
JmsListenerEndpoint
は、JMS エンドポイントのモデルを提供し、そのモデルのコンテナーの構成を担当します。このインフラストラクチャにより、JmsListener
アノテーションによって検出されるエンドポイントに加えて、プログラムでエンドポイントを構成できます。次の例は、その方法を示しています。
@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("myJmsEndpoint");
endpoint.setDestination("anotherQueue");
endpoint.setMessageListener(message -> {
// processing
});
registrar.registerEndpoint(endpoint);
}
}
前の例では、実際の MessageListener
を呼び出して提供する SimpleJmsListenerEndpoint
を使用しました。ただし、独自のエンドポイントバリアントを作成して、カスタム呼び出しメカニズムを記述することもできます。
@JmsListener
の使用を完全にスキップし、JmsListenerConfigurer
を介してエンドポイントのみをプログラムで登録できることに注意してください。
アノテーション付きエンドポイントメソッドシグネチャー
これまで、エンドポイントに単純な String
を注入していましたが、実際には非常に柔軟なメソッドシグネチャーを持つことができます。次の例では、Order
にカスタムヘッダーを挿入するように書き換えます。
@Component
public class MyService {
@JmsListener(destination = "myDestination")
public void processOrder(Order order, @Header("order_type") String orderType) {
...
}
}
JMS リスナーエンドポイントに挿入できる主な要素は次のとおりです。
生の
jakarta.jms.Message
またはそのサブクラス(受信メッセージ型と一致する場合)。ネイティブ JMS API へのオプションのアクセス用の
jakarta.jms.Session
(たとえば、カスタム応答を送信するため)。受信 JMS メッセージを表す
org.springframework.messaging.Message
。このメッセージには、カスタムヘッダーと標準ヘッダー(JmsHeaders
で定義されている)の両方が含まれていることに注意してください。@Header
- 標準の JMS ヘッダーを含む特定のヘッダー値を抽出するためのアノテーション付きメソッド引数。すべてのヘッダーにアクセスするために
java.util.Map
にも割り当て可能である必要がある@Headers
アノテーション付き引数。サポートされている型(
Message
またはSession
)のいずれでもないアノテーションのない要素は、ペイロードと見なされます。パラメーターに@Payload
アノテーションを付けることで、これを明示的にすることができます。@Valid
を追加して、検証をオンにすることもできます。
Spring の Message
抽象化を挿入する機能は、トランスポート固有の API に依存することなく、トランスポート固有のメッセージに格納されているすべての情報を活用するのに特に役立ちます。次の例は、その方法を示しています。
@JmsListener(destination = "myDestination")
public void processOrder(Message<Order> order) { ... }
メソッド引数の処理は DefaultMessageHandlerMethodFactory
によって提供され、追加のメソッド引数をサポートするためにさらにカスタマイズできます。変換と検証のサポートもそこでカスタマイズできます。
たとえば、処理する前に Order
が有効であることを確認したい場合、次の例に示すように、ペイロードに @Valid
でアノテーションを付け、必要なバリデーターを構成できます。
@Configuration
@EnableJms
public class AppConfig implements JmsListenerConfigurer {
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(myJmsHandlerMethodFactory());
}
@Bean
public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setValidator(myValidator());
return factory;
}
}
レスポンス管理
MessageListenerAdapter
の既存のサポートにより、メソッドは void
以外の戻り値型をすでに持つことができます。その場合、呼び出しの結果は jakarta.jms.Message
にカプセル化され、元のメッセージの JMSReplyTo
ヘッダーで指定された宛先、またはリスナーに設定されたデフォルトの宛先のいずれかに送信されます。メッセージング抽象化の @SendTo
アノテーションを使用して、デフォルトの宛先を設定できるようになりました。
processOrder
メソッドが OrderStatus
を返すようになったと仮定すると、次の例に示すように、自動的にレスポンスを送信するように記述できます。
@JmsListener(destination = "myDestination")
@SendTo("status")
public OrderStatus processOrder(Order order) {
// order processing
return status;
}
複数の @JmsListener アノテーション付きメソッドがある場合は、クラスレベルで @SendTo アノテーションを配置して、デフォルトの応答先を共有することもできます。 |
トランスポートに依存しない方法で追加のヘッダーを設定する必要がある場合は、次のような方法で代わりに Message
を返すことができます。
@JmsListener(destination = "myDestination")
@SendTo("status")
public Message<OrderStatus> processOrder(Order order) {
// order processing
return MessageBuilder
.withPayload(status)
.setHeader("code", 1234)
.build();
}
実行時にレスポンスの宛先を計算する必要がある場合は、実行時に使用する宛先も提供する JmsResponse
インスタンスにレスポンスをカプセル化できます。前の例を次のように書き換えることができます。
@JmsListener(destination = "myDestination")
public JmsResponse<Message<OrderStatus>> processOrder(Order order) {
// order processing
Message<OrderStatus> response = MessageBuilder
.withPayload(status)
.setHeader("code", 1234)
.build();
return JmsResponse.forQueue(response, "status");
}
最後に、優先度や存続時間など、レスポンスにいくつかの QoS 値を指定する必要がある場合、次の例に示すように、それに応じて JmsListenerContainerFactory
を構成できます。
@Configuration
@EnableJms
public class AppConfig {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
QosSettings replyQosSettings = new QosSettings();
replyQosSettings.setPriority(2);
replyQosSettings.setTimeToLive(10000);
factory.setReplyQosSettings(replyQosSettings);
return factory;
}
}