admin管理员组文章数量:1538180
消息转换器原理解析
在使用Spring
框架过程中,很多框架内部实现都涉及到消息转换器。
-
Spring MVC
框架中,将HTTP
请求信息转换为一个对象(@RequestBody
注解),将对象输出为HTTP响应信息(@ResponseBody
注解),都通过消息转换器HttpMessageConverter
来进行不同类型对象转换。 -
在操作Redis数据库时,一般选用
RedisTemplate
或StringRedisTemplate
,如何将对象存储到redis
中,就涉及到序列化方式的选择,不同序列化方式,结果不一样,虽然序列化器不是转换器,但作用大体是一样的。 -
在使用
RabbitMQ
消息队列中,生产者需要将对象转换成消息写进消息队列,消费者需要将消息转换成对象读取,都离不开消息转换器MessageConverter
进行消息转换。我们知道存储到数据到本地磁盘或者传输数据到网络另一端,都是以字节为最小单位进行的,所以在存储或传输对象时,最终是对象与具体字节数据相互转换,而类型转换器或序列化框架作用正是如此。
HTTP类型转换器
1、转换原理图
从上图知,发送消息时,消息转换器器作用是将对象转换为某一格式报文,然后将报文发送另一端(@ResponseBody
注解);接收消息时,消息转换器作用是将某一格式报文转换为对象(@RequestBody
注解)。
2、Spring MVC
框架内置了很多HTTP消息转换器,不同消息类型转换器处理不同Content-type
类型数据。如MappingJackson2HttpMessageConverter
处理请求类型为application/json
类型数据,StringHttpMessageConverter
处理类型为为text/html
类型数据等。在框架内部会根据不同请求类型值选择不同类型转换器进行消息转换。
3、RestTemplate VS HttpClient
RestTemplate
和HttpClient
都是处理HTTP
客户端工具,其中RestTemplate
内部内置消息转换器,在一定程度上减少代码开发,下面以一个例子来说明:
一、定义一个简单的restful接口
@RestController
![2018-11-30_10827](2018-11-30_10827.png)
public class UserController
{
@GetMapping(value = "/getUser")
public User getUserInfo(){
User user =new User();
user.setUserName("test");
return user ;
}
}
二、使用RestTemplate访问该服务
String url = "http://localhost:8080/getUser"; //请求地址
RestTemplate restTemplate = new RestTemplate();
User user = restTemplate.getForObject(url, User.class);
从这个例子可以看出,服务端通过@ResponseBody
注解,默认返回数据类型ContentType
值为application/json
,返回数据为json
格式数据,客户端RestTemplate
通过ContentType
选择内置转换器为MappingJackson2HttpMessageConverter
将json
格式数据转换为具体User
对象。
三、下图为RestTemplate
构造方法默认实现:
public RestTemplate() {
this.messageConverters = new ArrayList();
this.errorHandler = new DefaultResponseErrorHandler();
this.uriTemplateHandler = new DefaultUriTemplateHandler();
. . .
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
} else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
}
四、如果使用HttpClient
,还需要手动将json
数据转换为具体对象,调用以下代码:
User user =JsonUtil.fromJson(json, User.Class);
使用RestTemplate
,由于内置消息转换器,通过转换器自动完成json
格式数据与对象转换,不需要在写代码进行json格式字符串与对象转换。
Redis对象操作存储
1、RedisTemplate
和 StringRedisTemplate
默认序列化方式
RedisTemplate
默认采用JDK
序列化方式对对象进行存储,在控制台上看到是一堆乱码,由于控制台采用new String(byte [])
进行解码,两种序列化方式不一致,导致显示乱码。而StringRedisTemplate
采用string.getBytes(this.charset)
对字符串进行序列化
2、更改对象存储方式, 采用json
格式存储,默认提供了
Jackson2JsonRedisSerializer
和 GenericJackson2JsonRedisSerializer
两种序列化方式。
- 这两种序列化方式都能将对象转换成
json
格式存储在redis
服务器中 Jackson2JsonRedisSerializer
存储对象是不带类型,存储结构如下:
127.0.0.1:6379> get bbb
"{\"id\":11,\"name\":\"\xe5\xbc\xa0\xe4\xb8\x89\",\"no\":null}"
GenericJackson2JsonRedisSerializer
存储对象是带有Type类型的,通过类型可以知道要转换的类型,具体存储结构如下图(存储类型为:com.redis.springredis.Strudent
):
127.0.0.1:6379> get aaa
"[\"com.redis.springredis.Strudent\",{\"id\":11,\"name\":\"\xe5\xbc\xa0\xe4\xb8\
x89\"}]"
3、如何选择哪一种序列化类进行json
格式存储?
一般采用GenericJackson2JsonRedisSerializer
进行序列化存储统一处理。
我们只需要创建一个RedisTemplate
对象。如果选用Jackson2JsonRedisSerializer
,则对每个类型创建不同的RedisTemplate
对象。如存储User
对象,则需要创建User
类型的RedisTemplate
(new RedisTemplate<String, User>
),要存储Integer
类型对象,则需要创建Integer
类型的RedisTemplate
(new RedisTemplate<String, Integer>
),而GenericJackson2JsonRedisSerializer
解决此问题,因为存储时候存储具体类型,就知道要转换什么类型。
RabbitMQ消息转换器
1、转换原理图
生产者传送消息对象时,生产端消息转换器将对象转换成某一格式(如json格式)字节数据,消费者接收到对应字节数据,根据类型、消费端的消息转换器将字节数据转换成具体对象类型。
2、使用RabbitTemplate进行消息发送,默认消息转换器是什么
public RabbitTemplate() {
......
this.messageConverter = new SimpleMessageConverter();
this.messagePropertiesConverter = new DefaultMessagePropertiesConverter();
this.initDefaultStrategies();
}
从构造函数可以看出,默认转换器使用SimpleMessageConverter
,如何更改发送端消息转换器?
只需要在发送端配置文件中定义一个bean。
@Bean public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
相关源代码:
查看RabbitAutoConfiguration类中静态内部类RabbitTemplateConfiguration中rabbitTemplate生成
@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean({RabbitTemplate.class})
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
MessageConverter messageConverter = (MessageConverter)this.messageConverter.getIfUnique();
if (messageConverter != null) {
rabbitTemplate.setMessageConverter(messageConverter);
}}
......
}
由于指定了消息转换器为Jackson2JsonMessageConverter
,则messageConverter.getIfUnique()
方法生成对象指向定义的Jackson2JsonMessageConverter
转换器。
使用json
格式转换器,具体发送消息可以通过MQ控制台查看具体消息内容,如下:
3、AMQP Message 数据结构
public class Message implements Serializable {
private static final long serialVersionUID = -7177590352110605597L;
private static final String ENCODING = Charset.defaultCharset().name();
private static final SerializerMessageConverter SERIALIZER_MESSAGE_CONVERTER = new SerializerMessageConverter();
private final MessageProperties messageProperties;
private final byte[] body;}
从该类可以看出,body内容为发送对象消息根据发送端消息转换器序列化后的二进制内容。MessageProperties 为一个properties
文件,主要存储消息头部信息,如content_type
:表明消息格式为json
格式字符串,content_encoding
:表明字符串的编码格式为UTF-8
编码。TypeId:表明生产端发送对象是Student
类型对象(具体作用后面介绍)。
2、接收端如何接收数据:
接收端代码如下,那么消费端会使用哪一种方式接收数据,通过运行知道会通过process1进行消息处理
@Component @RabbitListener(queues = "hello")
public class Receiver1 {
public Receiver1(){
System.out.println("改造函数执行");
}
@RabbitHandler
public void process1(byte[] person) throws UnsupportedEncodingException {
System.out.println("自带消息转换器 : " + new String(person,"UTF-8"));
}
@RabbitHandler
public void process2(List<Person> person) {
System.out.println("集合 Receiver : " + person);
}
@RabbitHandler
public void process3(Student person) {
System.out.println("单独对象Receiver : " + person);
}
}
原理如下:
SimpleRabbitListenerContainerFactory
默认转换器为SimpleMessageConverter
,我们可以看看SimpleMessageConverter
的fromMessage
方法,该方法作用是将消息转换为具体对象。
从上图知,由于发送端content_type
为json格式,所以经过默认转换器转换后Object
值类型为byte[]
.然后根据消费者提供方法,进行反射,找到消费端处理方法含有入参参数为byte[]
字节数组方法,正好process1
方法满足条件。
如何更改接收端的消息转换器,方式和发送端类似,在配置类定义消息转换器,如果此时使用消息转换器为Jackson2JsonMessageConverter
,
从上图知,targetJavaType值为Student
类型对象,需要消费端存在Student对象,如果消费端不存在,则报转换异常,如果存在,则转换为相应Student对象。使用json转换器,要求服务端定义与客户端相同的类。
总结:
1、通过上述分析,合理使用消息中间件,能很大程度上减少开发中重复代码。
2、掌握消息中间件转换原理,能较快解决工作中遇到各种问题。
版权声明:本文标题:消息转换器原理解析 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1726966523a1092272.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论