skip to content

In Spring AMQP, what are an Exchange, a Queue, and a Binding, and how do you declare each as a Spring bean?

level: juniorimportance: must knowfreq 70%

answer

  1. publish to exchange, not queue
  2. Binding = exchange -> queue + key
  3. org.springframework.amqp.core beans
  4. RabbitAdmin declares on first connection
  5. new Queue() = durable by default

basics

~20 s

A Queue holds messages. An Exchange receives published messages and routes them. A Binding links an exchange to a queue with a routing rule. In Spring you declare each as a @Bean (Queue, DirectExchange, Binding).

solid answer

~40 s

In RabbitMQ, producers never publish to a queue directly — they publish to an Exchange, which routes the message to Queues according to Bindings. A Queue buffers messages for consumers; a Binding connects an exchange to a queue and (for most exchange types) carries a routing key that decides which messages match. In Spring AMQP (org.springframework.amqp.core), you model this topology as Spring beans: a Queue bean, an Exchange bean (DirectExchange, TopicExchange, or FanoutExchange), and a Binding bean built with BindingBuilder.bind(queue).to(exchange).with(routingKey). When these beans exist in the context, Spring's RabbitAdmin declares them on the broker automatically the first time a connection opens, so you rarely call the broker API by hand.

code

java · 23 lines
java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.*;

@Configuration
public class OrdersTopology {

    @Bean
    Queue ordersQueue() {
        return new Queue("orders.q"); // durable=true, exclusive=false, autoDelete=false
    }

    @Bean
    DirectExchange ordersExchange() {
        return new DirectExchange("orders.x");
    }

    @Bean
    Binding ordersBinding(Queue ordersQueue, DirectExchange ordersExchange) {
        return BindingBuilder.bind(ordersQueue)
                             .to(ordersExchange)
                             .with("orders.created"); // routing key
    }
}

go deeper

for a junior

Must know the exchange -> binding -> queue chain and that you declare each as a bean.

for a middle

Should know default queue properties and that RabbitAdmin does the declaring.

for a senior

Should discuss idempotent declaration and the PRECONDITION_FAILED mismatch gotcha.

for a principal

Frames topology-as-code ownership, version control of the broker model, and drift between beans and live broker state.

## The AMQP model RabbitMQ follows the AMQP 0-9-1 model, which separates *where you publish* from *where messages land*: - **Exchange** — the entry point. Producers publish a message plus a **routing key** to an exchange. The exchange itself stores nothing; it only decides which queues (if any) get a copy. - **Queue** — an ordered buffer that actually stores messages until a consumer acknowledges them. - **Binding** — a rule that connects an exchange to a queue. For most exchange types the binding includes a **binding key** (a.k.a. routing pattern); a message is delivered to the queue when its routing key matches the binding key according to that exchange's rules. A message with no matching binding is simply dropped (unless the publisher set the `mandatory` flag or the exchange has an alternate-exchange configured — details outside this leaf). ## Declaring topology in Spring AMQP All topology types live in `org.springframework.amqp.core`. You express the broker model as Spring beans: ```java @Bean Queue ordersQueue() { return new Queue("orders.q"); } // durable, non-exclusive, non-auto-delete by default @Bean DirectExchange ordersExchange() { return new DirectExchange("orders.x"); } @Bean Binding ordersBinding(Queue ordersQueue, DirectExchange ordersExchange) { return BindingBuilder.bind(ordersQueue).to(ordersExchange).with("orders.created"); } ``` - `new Queue(name)` defaults to **durable=true, exclusive=false, autoDelete=false**. Use the overloaded constructors or `QueueBuilder` (`QueueBuilder.durable(name).build()`) to change these or add arguments (TTL, DLX, max-length). - `BindingBuilder` is a fluent API: `bind(queue).to(exchange).with(key)`. ## How the beans reach the broker Spring does **not** create anything on the broker just because the beans exist. A `RabbitAdmin` (an `AmqpAdmin` implementation) scans the application context for `Queue`, `Exchange`, and `Binding` beans and **declares them on the broker when the connection is first established**. Spring Boot auto-configures a `RabbitAdmin` whenever a `ConnectionFactory` is present, so this usually just works. ## Key terms defined - **Routing key**: a string set by the publisher on each message. - **Binding key / pattern**: the string on the binding the exchange compares against. - **Durable**: survives a broker restart (the definition persists; message persistence is a separate per-message concern). - **Exclusive**: usable by only the declaring connection. - **Auto-delete**: removed when its last consumer/binding goes away. ## When to use Always declare topology as beans for anything your app owns, so it is created idempotently on startup and version-controlled in code. Fall back to declaring it on the listener with `@QueueBinding` when the queue is listener-specific. ## Gotcha If a queue/exchange already exists on the broker with **different properties** than your bean, redeclaration fails with a broker `PRECONDITION_FAILED` (channel-level) error — Spring will log it and the connection recovery loop may keep retrying. Keep bean definitions in sync with what is actually on the broker.

  • Who actually creates these queues and exchanges on the broker, and when?
    A RabbitAdmin (AmqpAdmin) bean — auto-configured by Spring Boot — scans the context for Queue/Exchange/Binding beans and declares them on the broker the first time a connection is opened, not at bean-instantiation time.
  • What are the default properties of new Queue("x")?
    Durable, non-exclusive, non-auto-delete. Use QueueBuilder or the multi-arg constructor to change them or add arguments like TTL or a dead-letter exchange.

saying these in an interview costs you the question

  • Saying producers publish directly to a queue (they publish to an exchange)
  • Thinking the beans are declared on the broker at context startup rather than on first connection
  • Assuming new Queue(name) is non-durable

context