RabbitMQ with Spring Boot
Spring Boot makes RabbitMQ integration exceptionally clean through Spring AMQP and the Spring Boot auto-configuration. With a few annotations and a starter dependency, Spring handles connection management, message conversion, retry logic, and listener containers automatically. This topic covers the Spring Boot way of working with RabbitMQ from the ground up.
Adding the Dependency
Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
Gradle
implementation 'org.springframework.boot:spring-boot-starter-amqp'
Configuration in application.properties
spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest spring.rabbitmq.virtual-host=/ # Publisher confirms spring.rabbitmq.publisher-confirm-type=correlated spring.rabbitmq.publisher-returns=true
Declaring Queues, Exchanges, and Bindings
Spring AMQP uses @Bean declarations to define RabbitMQ topology. Spring creates these resources automatically when the application starts.
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
public static final String ORDER_QUEUE = "orders";
public static final String ORDER_EXCHANGE = "app-events";
public static final String ROUTING_KEY = "order.#";
@Bean
public Queue orderQueue() {
return QueueBuilder.durable(ORDER_QUEUE).build();
}
@Bean
public TopicExchange orderExchange() {
return new TopicExchange(ORDER_EXCHANGE);
}
@Bean
public Binding orderBinding(Queue orderQueue, TopicExchange orderExchange) {
return BindingBuilder
.bind(orderQueue)
.to(orderExchange)
.with(ROUTING_KEY);
}
}
Publishing Messages with RabbitTemplate
RabbitTemplate is the Spring AMQP equivalent of a channel. It is auto-configured and injected wherever needed:
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final RabbitTemplate rabbitTemplate;
public OrderService(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void placeOrder(Order order) {
rabbitTemplate.convertAndSend(
RabbitConfig.ORDER_EXCHANGE,
"order.placed",
order
);
System.out.println("Order event published: " + order.getOrderId());
}
}
convertAndSend automatically serialises the Java object to JSON if Jackson is on the classpath and a Jackson2JsonMessageConverter is configured.
Configuring JSON Message Conversion
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
// ... queue/exchange/binding beans above ...
@Bean
public Jackson2JsonMessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate(
org.springframework.amqp.rabbit.connection.ConnectionFactory factory,
Jackson2JsonMessageConverter converter) {
RabbitTemplate template = new RabbitTemplate(factory);
template.setMessageConverter(converter);
return template;
}
}
Consuming Messages with @RabbitListener
The @RabbitListener annotation turns any method into a message consumer. Spring handles connection, channel, prefetch, and acknowledgements automatically:
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class OrderConsumer {
@RabbitListener(queues = RabbitConfig.ORDER_QUEUE)
public void handleOrder(Order order) {
System.out.println("Processing order: " + order.getOrderId());
// Spring automatically acks on normal return
// Throws exception → Spring nacks (requeues by default)
}
}
Error Handling with @RabbitListener
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException;
import org.springframework.stereotype.Component;
@Component
public class OrderConsumer {
@RabbitListener(queues = RabbitConfig.ORDER_QUEUE)
public void handleOrder(Order order) {
try {
processOrder(order);
} catch (TransientException e) {
// Requeue for retry
throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(
"Transient failure", e);
} catch (PermanentException e) {
// Dead-letter (do not requeue)
throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(
"Permanent failure", e);
}
}
}
Configuring Retry with SimpleRetryPolicy
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
org.springframework.amqp.rabbit.connection.ConnectionFactory factory) {
SimpleRabbitListenerContainerFactory f = new SimpleRabbitListenerContainerFactory();
f.setConnectionFactory(factory);
f.setPrefetchCount(1);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3)); // retry 3 times
f.setRetryTemplate(retryTemplate);
return f;
}
Dead Letter Queue Setup
@Bean
public Queue orderQueueWithDLQ() {
return QueueBuilder.durable("orders")
.withArgument("x-dead-letter-exchange", "dlx")
.withArgument("x-dead-letter-routing-key", "failed-order")
.build();
}
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange("dlx");
}
@Bean
public Queue deadLetterQueue() {
return QueueBuilder.durable("failed-orders").build();
}
@Bean
public Binding deadLetterBinding() {
return BindingBuilder
.bind(deadLetterQueue())
.to(deadLetterExchange())
.with("failed-order");
}
Testing with Spring Boot Test
@SpringBootTest
@ExtendWith(SpringExtension.class)
class OrderServiceTest {
@Autowired
private OrderService orderService;
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void testPlaceOrder() {
Order order = new Order(9999, "TestProduct", 1);
orderService.placeOrder(order);
// Receive and verify the message
Order received = (Order) rabbitTemplate.receiveAndConvert("orders", 5000);
assertNotNull(received);
assertEquals(9999, received.getOrderId());
}
}
Summary
Spring Boot auto-configures RabbitMQ through spring-boot-starter-amqp. Configure connection details in application.properties. Declare topology (queues, exchanges, bindings) as @Bean methods. Inject RabbitTemplate for publishing and use @RabbitListener for consuming. Configure JSON conversion with Jackson2JsonMessageConverter for automatic Java object serialisation. Use Spring's retry and dead letter queue support for production-grade error handling without writing custom retry loops.
