What do @EnableJms and @JmsListener do, and what is the minimal setup to consume messages from a queue in Spring?
answer
- @EnableJms -> BeanPostProcessor scans @JmsListener
- one container per endpoint
- default factory name = jmsListenerContainerFactory
- ConnectionFactory + factory + @EnableJms
- return value -> reply to JMSReplyTo
basics
~10 s@EnableJms turns on scanning for @JmsListener methods. You put @JmsListener(destination = "queue") on a bean method; Spring runs it for each incoming message. You also need a ConnectionFactory and a listener container factory bean.
solid answer
~40 s@EnableJms registers the infrastructure (a JmsListenerAnnotationBeanPostProcessor) that scans beans for @JmsListener-annotated methods and, for each, creates a message listener container. @JmsListener(destination = "orders") marks a method as an endpoint: Spring invokes it per message, converting the JMS Message into your method parameter (via a MessageConverter) and optionally binding headers. You need three things wired: a JMS ConnectionFactory, a JmsListenerContainerFactory bean (by default named jmsListenerContainerFactory), and @EnableJms somewhere. Spring Boot auto-configures @EnableJms and a DefaultJmsListenerContainerFactory when a ConnectionFactory bean is present, so you often just write the annotated method. The method can return a value, which Spring sends as a reply to the JMSReplyTo destination.
code
java · 23 lines@Configuration
@EnableJms
public class JmsConfig {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
ConnectionFactory connectionFactory) {
var factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrency("3-10");
return factory;
}
}
@Component
public class OrderListener {
@JmsListener(destination = "orders")
public void onOrder(Order order) {
// invoked once per message; payload converted via MessageConverter
System.out.println("received " + order.id());
}
}go deeper
Know the trio: @EnableJms + @JmsListener + a container factory, and that Boot auto-configures it.
Explain the BeanPostProcessor/registry mechanism and per-endpoint container creation.
Discuss converters, request/reply via JMSReplyTo, and choosing/naming the container factory.
Frame @JmsListener as one endpoint abstraction over pluggable containers and factories, and how the same model extends to durable subscriptions and reply routing.
## The pieces **JMS (Jakarta/Java Message Service)** is the standard API for talking to message brokers (ActiveMQ, Artemis, IBM MQ, etc.). Producers send `Message` objects to a **destination** (a `Queue` for point-to-point, or a `Topic` for publish/subscribe); consumers receive them. **`@EnableJms`** is a configuration annotation placed on a `@Configuration` class. It imports `JmsBootstrapConfiguration`, which registers a `JmsListenerAnnotationBeanPostProcessor` and a `JmsListenerEndpointRegistry`. The post-processor scans every Spring bean for methods annotated with **`@JmsListener`**; the registry owns and starts the resulting containers. **`@JmsListener`** marks a method as a message endpoint. Its key attributes: - `destination` — the queue/topic name (required). - `containerFactory` — which `JmsListenerContainerFactory` bean to use (defaults to the bean named `jmsListenerContainerFactory`). - `concurrency` — override the factory's concurrency, e.g. `"3-10"`. - `selector` — a JMS message selector (SQL-like filter on headers). - `subscription` / `id` — for durable topic subscriptions. For **each** `@JmsListener` method the infrastructure asks the `JmsListenerContainerFactory` to build a **message listener container** — the running object that opens a JMS `Session`, consumes messages, and dispatches each one to your method. ## What you must wire 1. A **`ConnectionFactory`** (broker connection pool). 2. A **`JmsListenerContainerFactory`** bean — normally `DefaultJmsListenerContainerFactory`, which produces `DefaultMessageListenerContainer` instances. By convention it is named `jmsListenerContainerFactory` so `@JmsListener` finds it without naming it. 3. **`@EnableJms`** on a config class. **Spring Boot** collapses this: with `spring-boot-starter-activemq`/`artemis` (or any `ConnectionFactory` bean) it auto-configures `@EnableJms` and a `DefaultJmsListenerContainerFactory`. You typically only write the annotated method and set properties like `spring.jms.listener.concurrency`. ## Message conversion & method signature Spring converts the incoming `jakarta.jms.Message` to your parameter type using a `MessageConverter` (default `SimpleMessageConverter`; use `MappingJackson2MessageConverter` for JSON). You can inject payload plus metadata: ```java @JmsListener(destination = "orders") public void handle(Order order, @Header("JMSCorrelationID") String cid) { ... } ``` If the method **returns a value**, Spring sends it as a reply to the message's `JMSReplyTo` destination (request/reply). ## Gotchas - Forgetting `@EnableJms` (outside Boot) → the annotation is silently ignored; no container is created. - The default factory name matters: if your factory bean has a different name, set `containerFactory` on `@JmsListener` or you get a startup error about no `jmsListenerContainerFactory`. - One container per endpoint by default; concurrency (multiple consumer threads) is a container setting, not a per-message thing.
- What happens if you annotate a method with @JmsListener but forget @EnableJms (in a plain Spring app, no Boot)?Nothing consumes: without @EnableJms the JmsListenerAnnotationBeanPostProcessor is never registered, so the annotation is ignored and no container is created. Spring Boot auto-configures @EnableJms so it works there implicitly.
- How does the payload get converted to your method parameter type?The container's MessageConverter converts the jakarta.jms.Message. Default is SimpleMessageConverter (String/byte[]/Serializable/Map). For JSON you set a MappingJackson2MessageConverter on the container factory.
saying these in an interview costs you the question
- Thinking @JmsListener works without any ConnectionFactory or container factory.
- Believing @EnableJms is optional in a non-Boot app.
- Confusing @JmsListener (consumer) with JmsTemplate (producer).