skip to content

@JmsListener & Listener Containers

@JmsListener runs your method for each message inside a container that manages connections, concurrency, acknowledgement and optionally transactions. Concurrency and acknowledge mode are the settings interviewers ask you to justify.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

5

What do @EnableJms and @JmsListener do, and what is the minimal setup to consume messages from a queue in Spring?

level: juniorimportance: must knowfreq 55%

answer

  1. @EnableJms -> BeanPostProcessor scans @JmsListener
  2. one container per endpoint
  3. default factory name = jmsListenerContainerFactory
  4. ConnectionFactory + factory + @EnableJms
  5. 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
java
@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

for a junior

Know the trio: @EnableJms + @JmsListener + a container factory, and that Boot auto-configures it.

for a middle

Explain the BeanPostProcessor/registry mechanism and per-endpoint container creation.

for a senior

Discuss converters, request/reply via JMSReplyTo, and choosing/naming the container factory.

for a principal

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).

context

open as a page

Compare DefaultMessageListenerContainer and SimpleMessageListenerContainer. When would you choose each, and how does concurrency differ?

level: middleimportance: must knowfreq 50%

basics

~20 s

DefaultMessageListenerContainer (DMLC) is the flexible default: it supports transactions, error recovery, and dynamic scaling of consumer threads (concurrency "lower-upper"). SimpleMessageListenerContainer (SMLC) is lightweight with a fixed number of consumers and no external transaction support. Use DMLC almost always.

open as a page

How does the concurrency setting work on a DefaultMessageListenerContainer, and what should you consider when tuning it (including for topics)?

level: middleimportance: should knowfreq 38%

basics

~20 s

On a DMLC, concurrency="lower-upper" (e.g. "3-10") sets how many consumer threads run: it starts at the lower number and scales up to the upper under load, then shrinks. More consumers means more parallel processing but also more sessions and possible message-ordering loss.

open as a page

Explain JMS acknowledge modes in a Spring listener container and how sessionTransacted changes message redelivery on exceptions.

level: seniorimportance: should knowfreq 42%

basics

~20 s

Acknowledge modes decide when a consumed message is marked done. With AUTO_ACKNOWLEDGE Spring acks after your listener returns successfully, so a thrown exception avoids the ack and the broker redelivers. With sessionTransacted=true the receive runs in a local JMS transaction that rolls back on exception, also causing redelivery.

open as a page

How do you achieve atomic processing across a JMS message and a database update in a Spring listener, and what are the constraints of using JtaTransactionManager?

level: principalimportance: should knowfreq 30%

basics

~20 s

Local sessionTransacted only covers JMS. For JMS + database atomicity you set an external JtaTransactionManager on a DefaultMessageListenerContainer, using an XA-capable ConnectionFactory and XADataSource so both resources commit or roll back together as one distributed (XA) transaction.

open as a page