消息转换器原理解析 | 您所在的位置:网站首页 › 内存条转换器吗有什么用途 › 消息转换器原理解析 |
消息转换器原理解析
在使用Spring框架过程中,很多框架内部实现都涉及到消息转换器。 Spring MVC框架中,将HTTP请求信息转换为一个对象(@RequestBody注解),将对象输出为HTTP响应信息(@ResponseBody注解),都通过消息转换器HttpMessageConverter来进行不同类型对象转换。 在操作Redis数据库时,一般选用RedisTemplate或StringRedisTemplate,如何将对象存储到redis中,就涉及到序列化方式的选择,不同序列化方式,结果不一样,虽然序列化器不是转换器,但作用大体是一样的。 在使用RabbitMQ消息队列中,生产者需要将对象转换成消息写进消息队列,消费者需要将消息转换成对象读取,都离不开消息转换器MessageConverter进行消息转换。 我们知道存储到数据到本地磁盘或者传输数据到网络另一端,都是以字节为最小单位进行的,所以在存储或传输对象时,最终是对象与具体字节数据相互转换,而类型转换器或序列化框架作用正是如此。 HTTP类型转换器1、转换原理图 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),要存储Integer类型对象,则需要创建Integer类型的RedisTemplate(new RedisTemplate),而GenericJackson2JsonRedisSerializer解决此问题,因为存储时候存储具体类型,就知道要转换什么类型。 RabbitMQ消息转换器1、转换原理图 从构造函数可以看出,默认转换器使用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) { System.out.println("集合 Receiver : " + person); } @RabbitHandler public void process3(Student person) { System.out.println("单独对象Receiver : " + person); } }原理如下: SimpleRabbitListenerContainerFactory默认转换器为SimpleMessageConverter,我们可以看看SimpleMessageConverter的fromMessage方法,该方法作用是将消息转换为具体对象。 如何更改接收端的消息转换器,方式和发送端类似,在配置类定义消息转换器,如果此时使用消息转换器为Jackson2JsonMessageConverter, 1、通过上述分析,合理使用消息中间件,能很大程度上减少开发中重复代码。 2、掌握消息中间件转换原理,能较快解决工作中遇到各种问题。 |
CopyRight 2018-2019 实验室设备网 版权所有 |