In Spring AMQP, what are an Exchange, a Queue, and a Binding, and how do you declare each as a Spring bean?
answer
- publish to exchange, not queue
- Binding = exchange -> queue + key
- org.springframework.amqp.core beans
- RabbitAdmin declares on first connection
- new Queue() = durable by default
basics
~20 sA 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 sIn 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 linesimport 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
Must know the exchange -> binding -> queue chain and that you declare each as a bean.
Should know default queue properties and that RabbitAdmin does the declaring.
Should discuss idempotent declaration and the PRECONDITION_FAILED mismatch gotcha.
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