1. 概述
RabbitMQ 是基于 AMQP 协议的消息中间件,在分布式系统中常用于解耦、异步处理、流量削峰等场景。SpringBoot 通过 spring-boot-starter-amqp 提供了对 RabbitMQ 的深度整合,让开发者能够以声明式或编程式的方式轻松操作消息队列。
本文将详细讲解:
-
队列创建的两种方式:@Bean 方式与 AmqpAdmin 方式
-
消息接收的两种方式:@RabbitListener 与 @RabbitHandler
每个知识点均配有详细的文字解释和完整的代码示例,参数含义会逐一说明。
2. 环境准备
2.1 添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.2 配置连接信息
yaml
spring:
rabbitmq:
host: 127.0.0.1 # RabbitMQ 服务地址
port: 5672 # 连接端口(注意不是管理界面端口 15672)
username: guest # 用户名
password: guest # 密码
virtual-host: / # 虚拟主机,默认为 /
# 可选:连接超时、重试等配置
connection-timeout: 5000
publisher-confirm-type: correlated # 开启发送确认
publisher-returns: true # 开启消息返回
3. 队列创建的两种方式
3.1 方式一:使用 @Bean 声明式创建
通过在 @Configuration 类中定义 Queue、Exchange、Binding 的 Bean,Spring 容器启动时会自动在 RabbitMQ 服务端创建这些组件。
3.1.1 基本示例
java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQBeanConfig {
// 1. 创建队列
@Bean
public Queue myQueue() {
// 参数详解:
// name: 队列名称,必须唯一
// durable: 是否持久化(true: 重启后队列依然存在)
// exclusive: 是否独占(true: 仅当前连接可用,连接关闭后队列删除)
// autoDelete: 是否自动删除(最后一个消费者取消后删除队列)
// 还有另一种构造方式:new Queue(name, durable, exclusive, autoDelete, arguments)
return new Queue("myQueue", true, false, false);
}
// 2. 创建交换机(以 DirectExchange 为例)
@Bean
public DirectExchange myExchange() {
// 参数详解:
// name: 交换机名称
// durable: 是否持久化
// autoDelete: 是否自动删除
// arguments: 扩展参数
return new DirectExchange("myExchange", true, false);
}
// 3. 创建绑定关系
@Bean
public Binding myBinding() {
return BindingBuilder
.bind(myQueue()) // 绑定哪个队列
.to(myExchange()) // 绑定到哪个交换机
.with("myRoutingKey"); // 路由键
}
}
3.1.2 带参数的队列(死信队列、TTL 等)
java
@Bean
public Queue ttlQueue() {
Map<String, Object> args = new HashMap<>();
// 设置队列中消息的 TTL(毫秒)
args.put("x-message-ttl", 60000);
// 设置死信交换机
args.put("x-dead-letter-exchange", "deadLetterExchange");
// 设置死信路由键
args.put("x-dead-letter-routing-key", "deadLetterKey");
// 设置队列最大长度(条数)
args.put("x-max-length", 1000);
// 设置队列最大容量(字节)
args.put("x-max-length-bytes", 10485760);
return new Queue("ttlQueue", true, false, false, args);
}
3.2 方式二:使用 AmqpAdmin 动态创建
AmqpAdmin 是 Spring 提供的用于管理 RabbitMQ 组件的接口,可以在运行时动态声明队列、交换机和绑定关系,适合需要根据业务逻辑动态创建临时队列的场景。
3.2.1 AmqpAdmin 常用方法
java
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DynamicQueueService {
@Autowired
private AmqpAdmin amqpAdmin;
// 动态创建队列
public void createQueue(String queueName, boolean durable, boolean exclusive, boolean autoDelete) {
Queue queue = new Queue(queueName, durable, exclusive, autoDelete);
amqpAdmin.declareQueue(queue);
}
// 动态创建交换机
public void createExchange(String exchangeName, String type, boolean durable) {
AbstractExchange exchange;
switch (type) {
case "direct":
exchange = new DirectExchange(exchangeName, durable, false);
break;
case "topic":
exchange = new TopicExchange(exchangeName, durable, false);
break;
case "fanout":
exchange = new FanoutExchange(exchangeName, durable, false);
break;
case "headers":
exchange = new HeadersExchange(exchangeName, durable, false);
break;
default:
throw new IllegalArgumentException("不支持的类型: " + type);
}
amqpAdmin.declareExchange(exchange);
}
// 动态创建绑定
public void createBinding(String queueName, String exchangeName, String routingKey) {
Binding binding = new Binding(
queueName,
Binding.DestinationType.QUEUE,
exchangeName,
routingKey,
null // arguments
);
amqpAdmin.declareBinding(binding);
}
// 删除队列
public boolean deleteQueue(String queueName) {
return amqpAdmin.deleteQueue(queueName);
}
// 清空队列消息(不删除队列)
public void purgeQueue(String queueName) {
amqpAdmin.purgeQueue(queueName, false);
}
}
3.2.2 动态创建带参数的队列
java
public void createQueueWithArgs(String queueName) {
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 30000); // 30秒过期
args.put("x-max-priority", 10); // 支持优先级
Queue queue = new Queue(queueName, true, false, false, args);
amqpAdmin.declareQueue(queue);
}
3.3 两种方式的对比:

4. 消息发送(RabbitTemplate)
在讲解消息接收之前,先了解如何发送消息,以便测试接收功能。
java
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MessageSender {
@Autowired
private RabbitTemplate rabbitTemplate;
// 简单发送字符串
public void send(String exchange, String routingKey, String message) {
rabbitTemplate.convertAndSend(exchange, routingKey, message);
}
// 发送带消息属性的消息
public void sendWithProperties(String exchange, String routingKey, String message) {
MessageProperties properties = new MessageProperties();
properties.setContentType("text/plain");
properties.setPriority(5); // 优先级
properties.setExpiration("60000"); // 消息过期时间(毫秒)
properties.setHeader("customKey", "customValue");
Message msg = MessageBuilder.withBody(message.getBytes(StandardCharsets.UTF_8))
.andProperties(properties)
.build();
rabbitTemplate.send(exchange, routingKey, msg);
}
// 发送 Java 对象(需要配置 Jackson 转换器)
public void sendObject(String exchange, String routingKey, Object obj) {
rabbitTemplate.convertAndSend(exchange, routingKey, obj);
}
}
5. 消息接收的两种方式
5.1 方式一:@RabbitListener 注解
@RabbitListener 可以标注在方法或类上,用于声明一个消息监听容器,自动监听指定队列。
5.1.1 基础用法
java
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class BasicListener {
// 监听单个队列,自动 ACK(默认)
@RabbitListener(queues = "myQueue")
public void listenSimple(String message) {
System.out.println("收到消息: " + message);
}
// 监听多个队列
@RabbitListener(queues = {"queueA", "queueB"})
public void listenMultiple(String message) {
System.out.println("从多个队列收到: " + message);
}
// 使用路由键动态创建队列(需要交换机已存在)
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = "autoQueue", durable = "true"),
exchange = @Exchange(value = "myExchange", type = ExchangeTypes.DIRECT),
key = "myRoutingKey"
)
)
public void listenWithBinding(String message) {
System.out.println("通过 @QueueBinding 自动创建: " + message);
}
}
5.1.2 @RabbitListener 常用参数详解

5.1.3 手动确认消息(ACK)
默认情况下,消息消费成功后会自动 ACK。若需要手动确认,需配置 acknowledge-mode 为 MANUAL,并在监听方法中引入 Channel 和 deliveryTag。
java
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class ManualAckListener {
@RabbitListener(queues = "myQueue", containerFactory = "manualAckContainerFactory")
public void listenManual(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 业务处理
System.out.println("处理消息: " + new String(message.getBody()));
// 手动确认(单条)
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
// 拒绝并重新入队(第二个参数 true 表示重新入队)
channel.basicNack(deliveryTag, false, true);
}
}
}
需要配置 containerFactory:
java
@Bean
public RabbitListenerContainerFactory<?> manualAckContainerFactory(
ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 手动确认
return factory;
}
5.2 方式二:@RabbitListener + @RabbitHandler 组合
当一个队列中包含多种类型(或格式)的消息时,可以在类上标注 @RabbitListener,然后在多个方法上标注 @RabbitHandler,根据消息类型自动路由到对应方法。
5.2.1 根据消息类型分发
java
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "multiTypeQueue")
public class MultiTypeListener {
// 处理 String 类型
@RabbitHandler
public void handleString(String message) {
System.out.println("字符串消息: " + message);
}
// 处理 Integer 类型
@RabbitHandler
public void handleInteger(Integer number) {
System.out.println("整数消息: " + number);
}
// 处理自定义 User 对象(需要配置 Jackson 转换器)
@RabbitHandler
public void handleUser(User user) {
System.out.println("用户对象: " + user.getName() + ", " + user.getAge());
}
// 处理 byte[] 类型(原始消息体)
@RabbitHandler
public void handleBytes(byte[] bytes) {
System.out.println("字节消息: " + new String(bytes));
}
// 处理 Message 原始对象(包含属性)
@RabbitHandler
public void handleRawMessage(Message message) {
String body = new String(message.getBody());
String contentType = message.getMessageProperties().getContentType();
System.out.println("原始消息 - 类型: " + contentType + ", 内容: " + body);
}
}
5.2.2 @RabbitHandler 方法参数匹配规则
- 根据方法参数的 Java 类型进行匹配(需要 MessageConverter 能将消息体转换成对应类型)
- 如果存在多个匹配的方法,会按照最具体的类型进行选择
- 可以通过 @Payload 和 @Headers 注解精确控制
java
@RabbitHandler
public void handleWithAnnotations(@Payload User user, @Headers Map<String, Object> headers) {
System.out.println("Payload: " + user);
System.out.println("Headers: " + headers);
}
5.2.3 配置 Jackson 消息转换器(支持对象)
java
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConverterConfig {
@Bean
public MessageConverter jacksonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
配置后,发送对象时会自动转为 JSON,接收时会自动反序列化为对应的 Java 类型。
6. 完整示例:综合运用
6.1 配置类(使用 @Bean 创建队列)
java
@Configuration
public class FullConfig {
@Bean
public Queue orderQueue() {
return new Queue("order.queue", true);
}
@Bean
public DirectExchange orderExchange() {
return new DirectExchange("order.exchange", true, false);
}
@Bean
public Binding orderBinding() {
return BindingBuilder.bind(orderQueue())
.to(orderExchange())
.with("order.create");
}
}
6.2 发送者
java
@Service
public class OrderSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendOrder(Order order) {
rabbitTemplate.convertAndSend("order.exchange", "order.create", order);
}
}
6.3 接收者(混合使用两种接收方式)
java
@Component
public class OrderListener {
// 方式一:简单接收
@RabbitListener(queues = "order.queue")
public void handleOrder(Order order) {
System.out.println("收到订单: " + order.getOrderId());
}
// 方式二:多类型处理器
@Component
@RabbitListener(queues = "order.queue")
public static class MultiHandler {
@RabbitHandler
public void onOrder(Order order) {
System.out.println("Handler 处理订单: " + order.getOrderId());
}
@RabbitHandler
public void onString(String str) {
System.out.println("意外收到字符串: " + str);
}
}
}
7. 常见问题与注意事项
-
队列是否存在:使用 @RabbitListener 时,若队列不存在且没有 bindings 自动创建,启动会报错。建议使用 @Bean 预先声明或 bindings 属性。
-
消息转换器:如果发送和接收使用不同的消息转换器,可能导致类型转换异常。全局统一配置 MessageConverter 为 Jackson2JsonMessageConverter 可避免大部分问题。
-
并发消费:concurrency 参数可以增加消费者实例,提高吞吐量,但要注意业务幂等性。
-
消息确认:生产环境建议使用手动 ACK,避免业务异常导致消息丢失。
-
死信队列:务必为重要业务队列配置死信交换机,防止消息堆积或异常丢失后无法追溯。
8. 总结
本文详细介绍了 SpringBoot 整合 RabbitMQ 的核心操作:
-
队列创建:@Bean 方式适合固定结构,AmqpAdmin 适合动态需求
-
消息接收:@RabbitListener 简单直接,配合 @RabbitHandler 可处理多类型消息