消息转换器原理解析
消息轉換器原理解析
在使用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接口
二、使用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存儲對象是不帶類型,存儲結構如下:
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進行消息發送,默認消息轉換器是什么
從構造函數可以看出,默認轉換器使用SimpleMessageConverter,如何更改發送端消息轉換器?
只需要在發送端配置文件中定義一個bean。
相關源代碼:
查看RabbitAutoConfiguration類中靜態內部類RabbitTemplateConfiguration中rabbitTemplate生成
由于指定了消息轉換器為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進行消息處理
原理如下:
SimpleRabbitListenerContainerFactory默認轉換器為SimpleMessageConverter,我們可以看看SimpleMessageConverter的fromMessage方法,該方法作用是將消息轉換為具體對象。
從上圖知,由于發送端content_type為json格式,所以經過默認轉換器轉換后Object值類型為byte[].然后根據消費者提供方法,進行反射,找到消費端處理方法含有入參參數為byte[] 字節數組方法,正好process1方法滿足條件。
如何更改接收端的消息轉換器,方式和發送端類似,在配置類定義消息轉換器,如果此時使用消息轉換器為Jackson2JsonMessageConverter,
從上圖知,targetJavaType值為Student類型對象,需要消費端存在Student對象,如果消費端不存在,則報轉換異常,如果存在,則轉換為相應Student對象。使用json轉換器,要求服務端定義與客戶端相同的類。
總結:
1、通過上述分析,合理使用消息中間件,能很大程度上減少開發中重復代碼。
2、掌握消息中間件轉換原理,能較快解決工作中遇到各種問題。
總結
- 上一篇: 网络空间安全是否有必要考研
- 下一篇: Python爬取淘宝图片