

🔥个人主页:代码不加冰(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:LeetCode刷题日记 ,苍穹外卖日记,SSM框架深入,JavaWeb,
✨命运的结局尽可永在,不屈的挑战却不可须臾或缺!
前言:
大家好我是代码不加冰,在这里祝大家端午安康,可惜在学校吃不到老家的粽子(只有蜜枣的那种)不知道有没有懂得,还是挺想念的。闲话就不多说了,由于是期末周,重心还是应对期末考试,我们这里主要是接着前面的RabbitMQ消息队列继续深入,忙着别的专题搁置了一段时间,我们继续拾起来,同样的还有算法和八股,都要带着学习了。
摘要:
本文介绍了RabbitMQ消息队列在Java中的两种主流声明方式:通过Spring AMQP(配置类或注解)和原生Java客户端。重点讲解了Spring AMQP的配置方法,包括交换机、队列的声明及绑定关系设置,并对比了两种方式的优缺点。
此外,详细说明了消息转换器的作用,推荐使用Jackson JSON转换器替代默认实现,以实现跨语言兼容和更好的可读性。文章还提供了核心参数的避坑指南,帮助开发者正确处理组件生命周期问题。最后展示了如何通过RabbitTemplate优雅地发送和接收自定义对象消息。
Java客户端组件声明:
在 Java 客户端中,声明队列和交换机主要有两种主流方式:
一种是使用 **RabbitMQ 原生 Java 客户端(AMQP Client)**另一种是使用 Spring Boot(Spring AMQP)。
在实际企业开发中,Spring Boot 方式最为常用,因为它通过注解或配置类将声明过程高度抽象化了。
方式一:使用 Spring Boot (Spring AMQP)
Spring AMQP 提供了两种声明方式:配置类(Bean 注入) 和 注解驱动。
1.1 通过配置类(@Configuration)声明
这种方式结构清晰,适合在项目启动时统一管理队列和交换机。
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
// 1. 声明交换机 (以 Topic 类型为例)
@Bean
public TopicExchange topicExchange() {
// 参数:交换机名称、是否持久化、是否自动删除
return new TopicExchange("boot.topic.exchange", true, false);
}
// 2. 声明队列
@Bean
public Queue myQueue() {
// 参数:队列名称、是否持久化、是否排他、是否自动删除
return new Queue("boot.topic.queue", true, false, false);
}
// 3. 声明绑定关系 (将队列绑定到交换机,并指定 Routing Key)
@Bean
public Binding bindingQueueToExchange(Queue myQueue, TopicExchange topicExchange) {
return BindingBuilder
.bind(myQueue)
.to(topicExchange)
.with("boot.#"); // 路由键匹配规则
}
}
1.2 通过注解(@RabbitListener)动态声明
如果我们不想写一堆配置类,可以在消费者监听消息时,直接在注解中动态创建。
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;
@Component
public class RabbitConsumer {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "boot.anno.queue", durable = "true"),
exchange = @Exchange(name = "boot.anno.exchange", type = "topic"),
key = "anno.#"
))
public void listen(String message) {
System.out.println("接收到消息: " + message);
}
}
方式二:使用 RabbitMQ 原生 Java 客户端
如果不使用 Spring 框架,纯 Java 项目需要通过 Channel 对象的 API 来显式声明。
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class RabbitNativeDeclare {
public static void main(String[] args) throws Exception {
// 1. 创建连接工厂并建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setUsername("guest");
factory.setPassword("guest");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
String exchangeName = "native.direct.exchange";
String queueName = "native.direct.queue";
String routingKey = "native.key";
// 2. 声明交换机
// 参数: exchange, type, durable, autoDelete, arguments
channel.exchangeDeclare(exchangeName, "direct", true, false, null);
// 3. 声明队列
// 参数: queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(queueName, true, false, false, null);
// 4. 绑定队列和交换机
// 参数: queue, exchange, routingKey
channel.queueBind(queueName, exchangeName, routingKey);
System.out.println("原生客户端:交换机、队列及绑定关系创建成功!");
}
}
}
核心参数避坑指南
无论使用哪种方式,声明时的几个布尔值(Boolean)参数至关重要,决定了组件的生命周期:
-
Durable (持久化):
-
true(推荐):RabbitMQ 服务重启后,交换机/队列依然存在。 -
false:重启后消失。
-
-
AutoDelete (自动删除):
-
true:当没有消费者连接这个队列(或没有队列绑定这个交换机)时,自动将其销毁。 -
false(推荐):即使没人用也一直保留。
-
-
Exclusive (排他队列):
true:只对首次声明它的连接(Connection)可见,且连接断开时队列会自动删除。通常用于临时、单消费者场景。
注意: 如果 RabbitMQ 中已经存在 同名的队列或交换机,但代码中声明的参数(如
durable状态)与服务端现有的不一致 ,程序会直接报错(406 PRECONDITION_FAILED)。修改参数时,需要先去 RabbitMQ 管控台手动删除旧的组件。
消息转换器:
在 RabbitMQ 的 Java 客户端中,消息转换器(MessageConverter) 是一个非常重要的组件。
简单来说:网络传输只能识别字节流(byte[]) ,而我们在 Java 代码里使用的是各种对象(如 String、User、Map 等)。
-
发送消息时 :需要把 Java 对象变成
byte[](序列化)。 -
接收消息时 :需要把
byte[]变回 Java 对象(反序列化)。
如果不使用消息转换器,你每次发消息都得手动写代码去转字节数组,非常痛苦。
1. 原生 Java 客户端 vs Spring AMQP
-
原生 Java 客户端(com.rabbitmq.client) :没有 内置的高级消息转换器。你必须手动将对象转成 JSON 字符串,再转成
byte[]才能发送。 -
Spring AMQP(Spring Boot) :自带 了
MessageConverter接口,并且提供了完美的自动化支持。只要配置好,你直接扔一个 Java 对象给它,它会自动帮你转。
因此,我们重点讲解 Spring Boot 中的消息转换器。
2. 默认的转换器
如果你在 Spring Boot 中不做任何配置,默认使用的是 SimpleMessageConverter。
-
它是怎么工作的 :它使用的是 Java 原生的序列化机制(
ObjectOutputStream)。 -
缺点:
-
可读性极差 :在 RabbitMQ 管控台看消息,全是
\xAC\xED\x00\x05sr\x00...这样的二进制乱码。 -
体积大:占用更多的网络带宽和磁盘空间。
-
安全性与跨语言问题:其他语言(如 Python、Go)很难解析 Java 原生序列化的数据。
-
3. 最佳实践:配置 JSON 消息转换器
在企业开发中,几乎 100% 都会将其替换为 Jackson JSON 转换器。
第一步:在配置类中注入 Bean
你只需要在项目中任何一个 @Configuration 类里,向 Spring 容器注入 Jackson2JsonMessageConverter即可。Spring AMQP 会自动识别并切换。
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 RabbitConfig {
// 只要配置了这个 Bean,发送和接收都会自动使用 JSON
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
第二步:发送对象
配置好后,使用 RabbitTemplate 发送消息时,你可以直接传一个自定义的对象(无需实现 Serializable 接口)
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendUserMessage() {
User user = new User("张三", 18);
// 自动转换为 JSON 字符串 {"name":"张三","age":18} 的字节流发送
rabbitTemplate.convertAndSend("amqp.exchange", "user.key", user);
}
第三步:优雅地接收对象
在消费者端,直接用对应的实体类接收,Spring 会自动把 JSON 解析回 Java 对象:
@RabbitListener(queues = "user.queue")
public void listenUser(User user) { // 👈 直接接收对象
System.out.println("收到用户消息:" + user.getName() + ", 年龄:" + user.getAge());
}
4. 它是怎么知道应该转成什么对象的
我们可能会好奇:RabbitMQ 队列里存的明明只是纯文本的 JSON 字符串,消费者怎么知道要把它反序列化成 User 类,而不是 Order 类
这就涉及到 AMQP 消息的核心结构了。一条消息由两部分组成:
-
Body(消息体):即 JSON 字符串。
-
Properties(消息头/属性) :关键就在这里
当使用 Jackson2JsonMessageConverter 发送消息时,它会在消息头中自动添加一个属性:
__TypeId__:com.example.domain.User(对象的全类名)
当消费者收到消息时,转换器会去读这个 __TypeId__,从而知道:我要把它转成 User 对象,然后帮你在后台默默完成转换。
结语:
如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!