How do you integrate Spring Boot with RabbitMQ (@RabbitListener, RabbitTemplate)?

9 minadvancedrabbitmqrabbittemplaterabbitlistener

Quick Answer

spring-amqp/spring-rabbit provides RabbitTemplate for sending messages to an exchange (with a routing key) and @RabbitListener for consuming messages from a queue, configured declaratively via application.yml and simple @Bean definitions for exchanges/queues/bindings. Unlike Kafka's topic-and-consumer-group model, RabbitMQ's routing is built around exchanges (which decide how a published message gets routed) and queues (which actually hold messages for consumers) connected by bindings — giving flexible routing patterns (direct, topic, fanout) suited to more traditional task-queue and pub-sub messaging scenarios.

Detailed Answer

spring-amqp/spring-rabbit provides Spring-friendly abstractions over RabbitMQ, following the AMQP model of exchanges, queues, and bindings rather than Kafka's partitioned-topic-and-consumer-group model.

Declaring the topology (exchange, queue, and the binding connecting them) as beans:

@Configuration
class RabbitConfig {
    @Bean
    Queue orderQueue() { return new Queue("order.queue", true); } // durable queue

    @Bean
    TopicExchange orderExchange() { return new TopicExchange("order.exchange"); }

    @Bean
    Binding binding(Queue orderQueue, TopicExchange orderExchange) {
        return BindingBuilder.bind(orderQueue).to(orderExchange).with("order.placed.*");
    }
}

Producing messages with RabbitTemplate:

@Service
class OrderEventPublisher {
    private final RabbitTemplate rabbitTemplate;

    void publish(OrderPlacedEvent event) {
        rabbitTemplate.convertAndSend("order.exchange", "order.placed.created", event);
        // exchange name, routing key, and the message payload (auto-converted, typically to/from JSON)
    }
}

Consuming messages with @RabbitListener:

@Component
class OrderEventConsumer {
    @RabbitListener(queues = "order.queue")
    void handleOrderPlaced(OrderPlacedEvent event) {
        inventoryService.reserveStock(event.orderId());
        // acknowledged automatically on successful return, by default
    }
}

Key conceptual difference from Kafka — routing via exchanges: RabbitMQ's exchange is the component that decides how an incoming published message gets routed to zero, one, or multiple queues, based on the exchange type and the message's routing key:

  • Direct exchange: routes to queues bound with an exact matching routing key.
  • Topic exchange: routes based on wildcard-pattern matching against the routing key (order.placed.* matching order.placed.created, order.placed.updated, etc.) — used in the example above.
  • Fanout exchange: routes a message to every queue bound to it, regardless of routing key — a simple broadcast/pub-sub pattern.

This gives RabbitMQ very flexible, fine-grained routing topologies, in contrast to Kafka's simpler (but very high-throughput, replay-capable) topic-and-partition model.

When RabbitMQ tends to fit well: traditional task-queue/work-distribution scenarios, complex routing requirements (multiple consumer types needing different subsets of messages based on routing patterns), and cases valuing RabbitMQ's mature per-message acknowledgment and flexible dead-lettering/retry semantics over Kafka's strengths in extremely high-throughput, replayable event-log style streaming.