### 准備
## 目標
了解 Spring AMQP 消息轉化實現
## 相關資源
Quick Tour for the impatient:<
http://docs.spring.io/spring-amqp/docs/1.7.3.RELEASE/reference/html/_reference.html#message-converters>
Sample code:<
https://github.com/gordonklg/study>,rabbitmq module
## 測試代碼
gordon.study.rabbitmq.springamqp.JsonMessage.java

### 分析
## MessageConverter
org.springframework.amqp.support.converter.MessageConverter 接口負責消息轉化,有兩個方法:
toMessage 方法將 Java 對象轉化為
org.springframework.amqp.core.Message;
fromMessage 方法將消息轉化為 Java 對象。

Message 類是 Spring AMQP 對消息的封裝,其 byte[] body 屬性代表消息內容, MessageProperties messageProperties 屬性代表消息屬性。
RabbitTemplate 類持有 MessageConverter 的引用,用來幫助 RabbitTemplate 處理 Message 與 Java 對象之間的轉化。
## Jackson2JsonMessageConverter
org.springframework.amqp.support.converter.Jackson2JsonMessageConverter 通過 Jackson 2.x 版本進行 Java 對象(POJOs)與 JSON 格式內容之間的轉化。
toMessage 方法實現邏輯:
- 通過 ObjectMapper 將 Java 對象轉化為 JSON 字符串,再將字符串轉為 byte[]
- 設置 MessageProperties,contentType 設為 application/json,contentEncoding 設為 UTF-8,contentLength 設為 byte[] 長度
- 向 MessageProperties 的 headers 屬性中添加 __TypeId__,其值為 Java 對象的類全名
調試中截取的 Message 實際值為:
(Body:'{"name":"Gordon","birthday":1498024107659,"tall":172}' MessageProperties [headers={__TypeId__=gordon.study.rabbitmq.springamqp.Student}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/json, contentEncoding=UTF-8, contentLength=53, deliveryMode=PERSISTENT, receivedDeliveryMode=null, expiration=null, priority=0, redelivered=null, receivedExchange=null, receivedRoutingKey=null, receivedDelay=null, deliveryTag=0, messageCount=null, consumerTag=null, consumerQueue=null])
其中 deliveryMode 默認值為 PERSISTENT(即默認持久化),這是 MessageProperties 定義的默認值。
fromMessage 方法實現邏輯:
- 如果入參 Message 對象的 MessageProperties 屬性為 null,或者消息屬性 contentType 值既不為空又不包含 json 關鍵字,則直接返回 Message 的 body (byte[])
- 從 MessageProperties 的 headers 屬性中讀出 __TypeId__ 的值,通過 Jackson 的 API 將之轉化為 JavaType 對象,再將 message body 轉化為 Java 對象
調試中截取的 Message 實際值為:
(Body:'{"name":"Gordon","birthday":1498032689741,"tall":172}' MessageProperties [headers={__TypeId__=gordon.study.rabbitmq.springamqp.Student}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/json, contentEncoding=UTF-8, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=spring, receivedDelay=null, deliveryTag=1, messageCount=0, consumerTag=null, consumerQueue=null])
## MessagePropertiesConverter
我們發現前面調試中截取的 Message 實際值在 send 與 receive 方法中並不完全相同。這是因為
RabbitMQ
中定義的 BasicProperties 只是 Spring AMQP 中定義的
MessageProperties
的一個子集,例如 contentLength 並不是
BasicProperties
的屬性,所以 receive 方法讀取出來的消息默認是不會有
contentLength
值得(因為存不到 RabbitMQ 里面)。
org.springframework.amqp.rabbit.support.MessagePropertiesConverter 接口就是用來提供
Spring AMQP MessageProperties 與 RabbitMQ BasicProperties 之間的轉化策略的。
Spring AMQP 中提供了 org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter 實現 MessagePropertiesConverter 接口。細節可以查看以下方法的實現:

## SimpleMessageConverter
SimpleMessageConverter 的 toMessage 方法根據傳入 Java 對象的類型設置 contentType 並將對象轉化為 byte[],支持以下 Java 類型:
- byte[]:contentType 設置為 application/octet-stream
- String:contentType 設置為 text/plain
- Serializable:contentType 設置為 application/x-java-serialized-object,body 為對象序列化得到的 byte[]
- other:contentType 為 MessageProperties 默認值 application/octet-stream,body 為 null。RabbitMQ 可以發送 body 為 null 的消息
而 fromMessage 方法也會根據消息的 contentType 決定如何解析消息體,確定方法最終返回的對象類型。如果
contentType
以 text 開頭的,則將 body 轉化為字符串;如果
contentType
為
application/x-java-serialized-object,則將 body 反序列化為對象;其它情況直接返回 byte[] 形式的 body。
由於通過 Jackson2JsonMessageConverter 發布的消息的 contentType 為 application/json,所以通過 SimpleMessageConverter 獲取到的消息的消息體是 byte[] 類型。