skip to content

How does JmsTemplate resolve a destination when you pass a String name, and how do queues vs topics come into play?

level: middleimportance: should knowfreq 50%

answer

  1. Destination arg = used as-is; String = via DestinationResolver
  2. DynamicDestinationResolver = createQueue/createTopic
  3. pubSubDomain false=queue, true=topic (name resolution only)
  4. JndiDestinationResolver for EE / pre-provisioned names
  5. defaultDestination(Name) for no-arg overloads

basics

~20 s

You can pass a Destination object directly, or a String name. A String is turned into a Destination by a DestinationResolver. The pubSubDomain flag decides whether that name means a queue (false) or a topic (true).

solid answer

~40 s

JmsTemplate send/receive methods are overloaded to take either a jakarta.jms.Destination or a String destination name. A Destination is used as-is. A String is passed to the configured DestinationResolver, whose default is DynamicDestinationResolver: it calls session.createQueue(name) or session.createTopic(name) depending on JmsTemplate's pubSubDomain flag (false=queue, the default; true=topic). 'Dynamic' means the broker may auto-create the destination if it doesn't exist. Alternatively you can plug in JndiDestinationResolver to look names up in JNDI (common in Jakarta EE app servers), optionally falling back to dynamic resolution. Because pubSubDomain is a single template-wide flag, a JmsTemplate is effectively either queue-oriented or topic-oriented; mixing both usually means two templates. There are also defaultDestination and defaultDestinationName properties used by the argument-less overloads.

code

java · 19 lines
java
@Configuration
public class JmsConfig {

    // A topic-oriented template: String names resolve to Topics
    @Bean
    public JmsTemplate topicJmsTemplate(ConnectionFactory cf) {
        JmsTemplate t = new JmsTemplate(cf);
        t.setPubSubDomain(true);                 // "events" -> createTopic("events")
        t.setDestinationResolver(new DynamicDestinationResolver());
        t.setDefaultDestinationName("events");
        return t;
    }

    // A queue-oriented template (default pubSubDomain=false)
    @Bean
    public JmsTemplate queueJmsTemplate(ConnectionFactory cf) {
        return new JmsTemplate(cf);              // "orders" -> createQueue("orders")
    }
}

go deeper

for a junior

Know you can pass either a Destination or a String name.

for a middle

Explain DestinationResolver, DynamicDestinationResolver, and the pubSubDomain queue/topic switch.

for a senior

Contrast dynamic vs JNDI resolution and call out the silent queue-instead-of-topic bug from a stale pubSubDomain flag.

for a principal

Reason about multitenant/custom DestinationResolver strategies and the operational danger of auto-created destinations from typos.

**The two ways to name a target.** Every `JmsTemplate` send/receive method comes in a `Destination`-typed and a `String`-typed variant: - `convertAndSend(Destination d, Object o)` — you already hold a `jakarta.jms.Destination` (a `Queue` or `Topic`), used verbatim. - `convertAndSend(String name, Object o)` — you pass a logical name; the template must turn it into a real `Destination`. **DestinationResolver.** The String path delegates to `org.springframework.jms.support.destination.DestinationResolver#resolveDestinationName(session, name, pubSubDomain)`. - **DynamicDestinationResolver** (the default): calls `session.createQueue(name)` when `pubSubDomain=false`, or `session.createTopic(name)` when `pubSubDomain=true`. These provider calls typically return a handle to an existing destination or let the broker create one on demand ('dynamic'). No JNDI needed. - **JndiDestinationResolver**: resolves the name through JNDI (e.g. `java:comp/env/jms/orders`). Caches lookups; `fallbackToDynamicDestination=true` lets it create a dynamic destination if the JNDI lookup fails. Used in Jakarta EE servers where admins pre-provision destinations. - You can implement `DestinationResolver` yourself for custom naming/multitenancy. **pubSubDomain — the queue/topic switch.** `JmsTemplate.pubSubDomain` is a single boolean: - `false` (default) → *point-to-point*: names resolve to Queues; each message is consumed by exactly one consumer. - `true` → *publish/subscribe*: names resolve to Topics; each message is delivered to every current subscriber. It affects **only String-name resolution** — if you pass a concrete `Destination`, the flag is irrelevant. Because it is template-wide, a single `JmsTemplate` is de facto queue-mode or topic-mode. To send to both, use two configured templates (or pass explicit `Destination` objects). **Default destination.** `defaultDestination` (a `Destination`) or `defaultDestinationName` (a String, resolved as above) back the no-target overloads like `convertAndSend(Object)` and `receiveAndConvert()`. Setting both is a configuration error. **Spring Boot defaults.** Boot's `JmsAutoConfiguration` creates a `JmsTemplate` with the default `DynamicDestinationResolver` and `pubSubDomain=false`. You can flip pub/sub globally with `spring.jms.pub-sub-domain=true`, but note that also affects listener containers via `DefaultJmsListenerContainerFactory` unless overridden. **Gotchas.** - Forgetting `pubSubDomain=true` when you meant a topic silently creates/uses a *queue* of the same name — messages then go to one consumer instead of fanning out, a classic 'my subscribers don't all get it' bug. - Dynamic resolution auto-creating typo'd destinations means a misspelled name won't error; it just creates a new empty destination. - JNDI resolution requires the name to be pre-provisioned; dynamic does not. **When to use which resolver.** DynamicDestinationResolver for standalone Spring Boot + a broker like ActiveMQ/Artemis. JndiDestinationResolver when running inside a Jakarta EE container that manages JMS resources.

  • You call convertAndSend("events", payload) expecting all subscribers to receive it, but only one consumer gets each message. Why?
    pubSubDomain is left at its default false, so the String 'events' resolved to a Queue (point-to-point), not a Topic. Set pubSubDomain=true (or spring.jms.pub-sub-domain=true) on the template so the name resolves via createTopic.
  • What does 'dynamic' mean in DynamicDestinationResolver, and what risk does it carry?
    It resolves a name by calling session.createQueue/createTopic, which returns or auto-creates the destination on the broker without pre-provisioning. The risk: a misspelled destination name won't fail — it silently creates a new empty destination, so messages go nowhere useful.

saying these in an interview costs you the question

  • Thinking pubSubDomain affects sends when you pass an explicit Destination object (it only affects String-name resolution)
  • Believing you can send to a queue and a topic from the same template just by changing the name (pubSubDomain is template-wide)
  • Assuming a wrong destination name throws an error (dynamic resolution just creates it)

context