249 lines
9.2 KiB
Plaintext
249 lines
9.2 KiB
Plaintext
[[jms-annotated]]
|
|
= Annotation-driven Listener Endpoints
|
|
|
|
The easiest way to receive a message asynchronously is to use the annotated listener
|
|
endpoint infrastructure. In a nutshell, it lets you expose a method of a managed
|
|
bean as a JMS listener endpoint. The following example shows how to use it:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Component
|
|
public class MyService {
|
|
|
|
@JmsListener(destination = "myDestination")
|
|
public void processOrder(String data) { ... }
|
|
}
|
|
----
|
|
|
|
The idea of the preceding example is that, whenever a message is available on the
|
|
`jakarta.jms.Destination` `myDestination`, the `processOrder` method is invoked
|
|
accordingly (in this case, with the content of the JMS message, similar to
|
|
what the xref:integration/jms/receiving.adoc#jms-receiving-async-message-listener-adapter[`MessageListenerAdapter`]
|
|
provides).
|
|
|
|
The annotated endpoint infrastructure creates a message listener container
|
|
behind the scenes for each annotated method, by using a `JmsListenerContainerFactory`.
|
|
Such a container is not registered against the application context but can be easily
|
|
located for management purposes by using the `JmsListenerEndpointRegistry` bean.
|
|
|
|
TIP: `@JmsListener` is a repeatable annotation on Java 8, so you can associate
|
|
several JMS destinations with the same method by adding additional `@JmsListener`
|
|
declarations to it.
|
|
|
|
|
|
[[jms-annotated-support]]
|
|
== Enable Listener Endpoint Annotations
|
|
|
|
To enable support for `@JmsListener` annotations, you can add `@EnableJms` to one of
|
|
your `@Configuration` classes, as the following example shows:
|
|
|
|
include-code::./JmsConfiguration[tag=snippet,indent=0]
|
|
|
|
By default, the infrastructure looks for a bean named `jmsListenerContainerFactory`
|
|
as the source for the factory to use to create message listener containers. In this
|
|
case (and ignoring the JMS infrastructure setup), you can invoke the `processOrder`
|
|
method with a core pool size of three threads and a maximum pool size of ten threads.
|
|
|
|
You can customize the listener container factory to use for each annotation or you can
|
|
configure an explicit default by implementing the `JmsListenerConfigurer` interface.
|
|
The default is required only if at least one endpoint is registered without a specific
|
|
container factory. See the javadoc of classes that implement
|
|
{spring-framework-api}/jms/annotation/JmsListenerConfigurer.html[`JmsListenerConfigurer`]
|
|
for details and examples.
|
|
|
|
|
|
[[jms-annotated-programmatic-registration]]
|
|
== Programmatic Endpoint Registration
|
|
|
|
`JmsListenerEndpoint` provides a model of a JMS endpoint and is responsible for configuring
|
|
the container for that model. The infrastructure lets you programmatically configure endpoints
|
|
in addition to the ones that are detected by the `JmsListener` annotation.
|
|
The following example shows how to do so:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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);
|
|
}
|
|
}
|
|
----
|
|
|
|
In the preceding example, we used `SimpleJmsListenerEndpoint`, which provides the actual
|
|
`MessageListener` to invoke. However, you could also build your own endpoint variant
|
|
to describe a custom invocation mechanism.
|
|
|
|
Note that you could skip the use of `@JmsListener` altogether
|
|
and programmatically register only your endpoints through `JmsListenerConfigurer`.
|
|
|
|
|
|
[[jms-annotated-method-signature]]
|
|
== Annotated Endpoint Method Signature
|
|
|
|
So far, we have been injecting a simple `String` in our endpoint, but it can actually
|
|
have a very flexible method signature. In the following example, we rewrite it to inject the `Order` with
|
|
a custom header:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@Component
|
|
public class MyService {
|
|
|
|
@JmsListener(destination = "myDestination")
|
|
public void processOrder(Order order, @Header("order_type") String orderType) {
|
|
...
|
|
}
|
|
}
|
|
----
|
|
|
|
The main elements you can inject in JMS listener endpoints are as follows:
|
|
|
|
* The raw `jakarta.jms.Message` or any of its subclasses (provided that it
|
|
matches the incoming message type).
|
|
* The `jakarta.jms.Session` for optional access to the native JMS API (for example, for sending
|
|
a custom reply).
|
|
* The `org.springframework.messaging.Message` that represents the incoming JMS message.
|
|
Note that this message holds both the custom and the standard headers (as defined
|
|
by `JmsHeaders`).
|
|
* `@Header`-annotated method arguments to extract a specific header value, including
|
|
standard JMS headers.
|
|
* A `@Headers`-annotated argument that must also be assignable to `java.util.Map` for
|
|
getting access to all headers.
|
|
* A non-annotated element that is not one of the supported types (`Message` or
|
|
`Session`) is considered to be the payload. You can make that explicit by annotating
|
|
the parameter with `@Payload`. You can also turn on validation by adding an extra
|
|
`@Valid`.
|
|
|
|
The ability to inject Spring's `Message` abstraction is particularly useful to benefit
|
|
from all the information stored in the transport-specific message without relying on
|
|
transport-specific API. The following example shows how to do so:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@JmsListener(destination = "myDestination")
|
|
public void processOrder(Message<Order> order) { ... }
|
|
----
|
|
|
|
Handling of method arguments is provided by `DefaultMessageHandlerMethodFactory`, which you can
|
|
further customize to support additional method arguments. You can customize the conversion and validation
|
|
support there as well.
|
|
|
|
For instance, if we want to make sure our `Order` is valid before processing it, we can
|
|
annotate the payload with `@Valid` and configure the necessary validator, as the following example shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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;
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
[[jms-annotated-response]]
|
|
== Response Management
|
|
|
|
The existing support in xref:integration/jms/receiving.adoc#jms-receiving-async-message-listener-adapter[`MessageListenerAdapter`]
|
|
already lets your method have a non-`void` return type. When that is the case, the result of
|
|
the invocation is encapsulated in a `jakarta.jms.Message`, sent either in the destination specified
|
|
in the `JMSReplyTo` header of the original message or in the default destination configured on
|
|
the listener. You can now set that default destination by using the `@SendTo` annotation of the
|
|
messaging abstraction.
|
|
|
|
Assuming that our `processOrder` method should now return an `OrderStatus`, we can write it
|
|
to automatically send a response, as the following example shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@JmsListener(destination = "myDestination")
|
|
@SendTo("status")
|
|
public OrderStatus processOrder(Order order) {
|
|
// order processing
|
|
return status;
|
|
}
|
|
----
|
|
|
|
TIP: If you have several `@JmsListener`-annotated methods, you can also place the `@SendTo`
|
|
annotation at the class level to share a default reply destination.
|
|
|
|
If you need to set additional headers in a transport-independent manner, you can return a
|
|
`Message` instead, with a method similar to the following:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@JmsListener(destination = "myDestination")
|
|
@SendTo("status")
|
|
public Message<OrderStatus> processOrder(Order order) {
|
|
// order processing
|
|
return MessageBuilder
|
|
.withPayload(status)
|
|
.setHeader("code", 1234)
|
|
.build();
|
|
}
|
|
----
|
|
|
|
If you need to compute the response destination at runtime, you can encapsulate your response
|
|
in a `JmsResponse` instance that also provides the destination to use at runtime. We can rewrite the previous
|
|
example as follows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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");
|
|
}
|
|
----
|
|
|
|
Finally, if you need to specify some QoS values for the response such as the priority or
|
|
the time to live, you can configure the `JmsListenerContainerFactory` accordingly,
|
|
as the following example shows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
@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;
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
|