消息转换器
一、默认转换器存在问题
在 SpringAMQP 的发送方法中,接收消息的类型是 Object,也就是说我们可以发送任意对象类型的消息,Spring 会帮我们序列化为字节后发送给 MQ,接收消息的时候,还会把字节反序列化为 Java 对象。
只不过,默认情况下 Spring 采用的序列化方式是 JDK 序列化。众所周知,JDK 序列化存在下列问题:
- 数据体积过大
- 有安全漏洞
- 可读性差
二、测试默认转换器
1.声明队列
因为我们向在 mq 客户端看到消息,所以我们在 consumer 配置类中利用 @Bean 的方式声明一个队列:
package cn.itcast.mq.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 温柔哥
* @create 2024-02-02 10:25
*/
@Configuration
public class FanoutConfig {
// 测试默认转换器
@Bean
public Queue objectQueue() {
return new Queue("object.queue");
}
}
启动 consumer 服务,查看 mq 客户端声明队列是否成功
2.测试发送者发消息
使用 Map 对象当作消息发送到队列 object.queue
// 测试转换器
@Test
public void testSendObjectQueue() {
Map<String, Object> message = new HashMap<>();
message.put("name", "gentlebrother");
message.put("age", 22);
rabbitTemplate.convertAndSend("object.queue", message);
}
运行该测试类,到 mq 客户端查看消息
3.分析结果
可以看到,消息内容变成了一大长串的字符串,根本看不懂是什么。
然后再看一下 content_type(消息类型)为 java 序列化,这时再想一下,rabbitmq 只支持字节类型的消息,而 SpringAMQP 却允许我们发送 Object 对象类型的消息,这就说明了 Spring 会将我们的对象做序列化。
默认用的序列化方式就是 jdk 的序列化,但是我们知道 ObjectOutputStream 这种序列化有很多缺点,第一是性能比较差,第二是安全性有问题,容易出现注入的问题,第三是数据长度也太长了,消息体越大,消息传送速度越慢,还占用额外的内存空间
三、配置JSON转换器
Spring 的对消息对象的处理是由 org.springframework.amqp.support.converter.MessageConverter 来处理的。而默认实现是 SimpleMessageConverter,基于 JDK 的 ObjectOutputStream 完成序列化。
如果要修改只需要定义一个 MessageConverter 类型的 Bean 即可。推荐用 JSON 方式序列化,步骤如下:
1.引入依赖
因为无论是消费者需要反序列化为 java 对象,还是发送者需要进行序列化,都需要使用 jackon 的依赖,故我们直接在父工程中引入依赖:
<!-- jackson 依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
2.在publisher服务声明bean
我们在 publisher 服务声明 MessageConverter,因为启动类也是一个配置类,所以我们直接在 publisher 服务的启动类声明即可
package cn.itcast.mq;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class PublisherApplication {
public static void main(String[] args) {
SpringApplication.run(PublisherApplication.class);
}
// 配置JSON转换器
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
3.测试发送消息是否JSON序列化成功
首先到 mq 客户端将 object.queue 队列中的之前的消息清空
运行之前写好的测试发送者发送消息的方法,重新发送消息,可以看到发送的消息成功转换成 JSON 格式
4.在consumer服务声明bean
直接在 consumer 服务的启动类声明即可
package cn.itcast.mq;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
// 配置JSON转换器
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
5.定义一个消费者
定义一个消费者,监听 object.queue 队列并消费消息,消息类型和我们发送消息使用的类型一样,都是 Map 类型
package cn.itcast.mq.listener;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author 温柔哥
* @create 2024-02-01 16:01
*/
@Component
public class SpringRabbitListener {
// 测试转换器
@RabbitListener(queues = "object.queue")
public void listenObjectQueue1(Map<String, Object> message) {
System.out.println("消费者接受到 object.queue 的消息:【" + message + "】");
}
}
重启 consumer 服务,查看控制台消息,可以看到已经将消息反序列化为了 java 对象
四、总结
SpringAMQP中消息的序列化和反序列化是怎么实现的?
利用MessageConverter实现的,默认是JDK的序列化
注意发送方与接收方必须使用相同的MessageConverter