[Spring整合JMS(三)——MessageConverter介紹](http://elim.iteye.com/blog/1900937)
[TOC=1,3]
## 1.4?????消息轉換器MessageConverter
MessageConverter的作用主要有兩方面,一方面它可以把我們的非標準化Message對象轉換成我們的目標Message對象,這主要是用在發送消息的時候;另一方面它又可以把我們的Message對象轉換成對應的目標對象,這主要是用在接收消息的時候。
下面我們就拿發送一個對象消息來舉例,假設我們有這樣一個需求:我們平臺有一個發送郵件的功能,進行發送的時候我們只是把我們的相關信息封裝成一個JMS消息,然后利用JMS進行發送,在對應的消息監聽器進行接收到的消息處理時才真正的進行消息發送。
假設我們有這么一個Email對象:
Java代碼??
1. public?class?Email?implements?Serializable?{??
2. ???
3. ????private?static?final?long?serialVersionUID?=?-658250125732806493L;??
4. ???
5. ????private?String?receiver;??
6. ????private?String?title;??
7. ????private?String?content;??
8. ???
9. ????public?Email(String?receiver,?String?title,?String?content)?{??
10. ????????this.receiver?=?receiver;??
11. ????????this.title?=?title;??
12. ????????this.content?=?content;??
13. ????}??
14. ???
15. ????public?String?getReceiver()?{??
16. ????????return?receiver;??
17. ????}??
18. ???
19. ????public?void?setReceiver(String?receiver)?{??
20. ????????this.receiver?=?receiver;??
21. ????}??
22. ???
23. ????public?String?getTitle()?{??
24. ????????return?title;??
25. ????}??
26. ???
27. ????public?void?setTitle(String?title)?{??
28. ????????this.title?=?title;??
29. ????}??
30. ???
31. ????public?String?getContent()?{??
32. ????????return?content;??
33. ????}??
34. ???
35. ????public?void?setContent(String?content)?{??
36. ????????this.content?=?content;??
37. ????}??
38. ???
39. ????@Override??
40. ????public?String?toString()?{??
41. ????????StringBuilder?builder?=?new?StringBuilder();??
42. ????????builder.append("Email?[receiver=").append(receiver).append(",?title=")??
43. ????????????????.append(title).append(",?content=").append(content).append("]");??
44. ????????return?builder.toString();??
45. ????}??
46. ??????
47. }??
???????這個Email對象包含了一個簡單的接收者email地址、郵件主題和郵件內容。我們在發送的時候就把這個對象封裝成一個ObjectMessage進行發送。代碼如下所示:
Java代碼??
1. public?class?ProducerServiceImpl?implements?ProducerService?{??
2. ???
3. ????@Autowired??
4. ????private?JmsTemplate?jmsTemplate;??????
5. ??
6. ????public?void?sendMessage(Destination?destination,?final?Serializable?obj)?{??
7. ????????jmsTemplate.send(destination,?new?MessageCreator()?{??
8. ???
9. ????????????public?Message?createMessage(Session?session)?throws?JMSException?{??
10. ????????????????ObjectMessage?objMessage?=?session.createObjectMessage(obj);??
11. ????????????????return?objMessage;??
12. ????????????}??
13. ??????????????
14. ????????});??
15. ????}??
16. ???
17. }??
???????這是對應的在沒有使用MessageConverter的時候我們需要new一個MessageCreator接口對象,然后在其抽象方法createMessage內部使用session創建一個對應的消息。在使用了MessageConverter的時候我們在使用JmsTemplate進行消息發送時只需要調用其對應的convertAndSend方法即可。如:
?
Java代碼??
1. public?void?sendMessage(Destination?destination,?final?Serializable?obj)?{??
2. ????//未使用MessageConverter的情況??
3. ????/*jmsTemplate.send(destination,?new?MessageCreator()?{?
4. ?
5. ????????public?Message?createMessage(Session?session)?throws?JMSException?{?
6. ????????????ObjectMessage?objMessage?=?session.createObjectMessage(obj);?
7. ????????????return?objMessage;?
8. ????????}?
9. ?????????
10. ????});*/??
11. ????//使用MessageConverter的情況??
12. ????jmsTemplate.convertAndSend(destination,?obj);??
13. }??
這樣JmsTemplate就會在其內部調用預定的MessageConverter對我們的消息對象進行轉換,然后再進行發送。
???????這個時候我們就需要定義我們的MessageConverter了。要定義自己的MessageConverter很簡單,只需要實現Spring為我們提供的MessageConverter接口即可。我們先來看一下MessageConverter接口的定義:
Java代碼??
1. public?interface?MessageConverter?{??
2. ???
3. ????Message?toMessage(Object?object,?Session?session)?throws?JMSException,?MessageConversionException;??
4. ???
5. ????Object?fromMessage(Message?message)?throws?JMSException,?MessageConversionException;??
6. ???
7. }??
???????我們可以看到其中一共定義了兩個方法fromMessage和toMessage,fromMessage是用來把一個JMS Message轉換成對應的Java對象,而toMessage方法是用來把一個Java對象轉換成對應的JMS Message。因為我們已經知道上面要發送的對象就是一個Email對象,所以在這里我們就簡單地定義一個EmailMessageConverter用來把Email對象和對應的ObjectMessage進行轉換,其代碼如下:
Java代碼??
1. import?javax.jms.JMSException;??
2. import?javax.jms.Message;??
3. import?javax.jms.ObjectMessage;??
4. import?javax.jms.Session;??
5. ???
6. import?org.springframework.jms.support.converter.MessageConversionException;??
7. import?org.springframework.jms.support.converter.MessageConverter;??
8. ???
9. public?class?EmailMessageConverter?implements?MessageConverter?{??
10. ???
11. ????public?Message?toMessage(Object?object,?Session?session)??
12. ????????????throws?JMSException,?MessageConversionException?{??
13. ????????return?session.createObjectMessage((Serializable)?object);??
14. ????}??
15. ???
16. ????public?Object?fromMessage(Message?message)?throws?JMSException,??
17. ????????????MessageConversionException?{??
18. ????????ObjectMessage?objMessage?=?(ObjectMessage)?message;??
19. ????????return?objMessage.getObject();??
20. ????}??
21. ???
22. }??
???????這樣當我們利用JmsTemplate的convertAndSend方法發送一個Email對象的時候就會把對應的Email對象當做參數調用我們定義好的EmailMessageConverter的toMessage方法。
???????定義好我們的EmailMessageConverter之后就需要指定我們用來發送Email對象的JmsTemplate對象的messageConverter為EmailMessageConverter,這里我們在Spring的配置文件中定義JmsTemplate bean的時候就指定:
Xml代碼??
1. ??
2. bean?id="jmsTemplate"?class="org.springframework.jms.core.JmsTemplate">??
3. ??????
4. ????property?name="connectionFactory"?ref="connectionFactory"/>??
5. ??????
6. ????property?name="messageConverter"?ref="emailMessageConverter"/>??
7. bean>??
8. ??
9. bean?id="emailMessageConverter"?class="com.tiantian.springintejms.converter.EmailMessageConverter"/>??
???????到此我們的MessageConverter就定義好了,也能夠進行使用了,接著我們來進行測試一下,定義測試代碼如下所示:
Java代碼??
1. @Test??
2. public?void?testObjectMessage()?{??
3. ????Email?email?=?new?Email("zhangsan@xxx.com",?"主題",?"內容");??
4. ????producerService.sendMessage(destination,?email);??
5. }??
???????上面destination對應的接收處理的MessageListener方法如下所示:
?
Java代碼??
1. public?class?ConsumerMessageListener?implements?MessageListener?{??
2. ???
3. ????public?void?onMessage(Message?message)?{??
4. ??????????
5. ????????if?(message?instanceof?ObjectMessage)?{??
6. ????????????ObjectMessage?objMessage?=?(ObjectMessage)?message;??
7. ????????????try?{??
8. ????????????????Object?obj?=?objMessage.getObject();??
9. ????????????????Email?email?=?(Email)?obj;??
10. ????????????????System.out.println("接收到一個ObjectMessage,包含Email對象。");??
11. ????????????????System.out.println(email);??
12. ????????????}?catch?(JMSException?e)?{??
13. ????????????????e.printStackTrace();??
14. ????????????}??
15. ????????}??
16. ????}??
17. ???
18. }??
???????之前說了MessageConverter有兩方面的功能,除了把Java對象轉換成對應的Jms Message之外還可以把Jms Message轉換成對應的Java對象。我們看上面的消息監聽器在接收消息的時候接收到的就是一個Jms Message,如果我們要利用MessageConverter來把它轉換成對應的Java對象的話,只能是我們往里面注入一個對應的MessageConverter,然后在里面手動的調用,如:
Java代碼??
1. public?class?ConsumerMessageListener?implements?MessageListener?{??
2. ???
3. ????private?MessageConverter?messageConverter;??
4. ??????
5. ????public?void?onMessage(Message?message)?{??
6. ??????????
7. ????????if?(message?instanceof?ObjectMessage)?{??
8. ????????????ObjectMessage?objMessage?=?(ObjectMessage)?message;??
9. ????????????try?{??
10. ????????????????/*Object?obj?=?objMessage.getObject();?
11. ????????????????Email?email?=?(Email)?obj;*/??
12. ????????????????Email?email?=?(Email)?messageConverter.fromMessage(objMessage);??
13. ????????????????System.out.println("接收到一個ObjectMessage,包含Email對象。");??
14. ????????????????System.out.println(email);??
15. ????????????}?catch?(JMSException?e)?{??
16. ????????????????e.printStackTrace();??
17. ????????????}??
18. ??????????????
19. ????????}??
20. ????}??
21. ???
22. ????public?MessageConverter?getMessageConverter()?{??
23. ????????return?messageConverter;??
24. ????}??
25. ???
26. ????public?void?setMessageConverter(MessageConverter?messageConverter)?{??
27. ????????this.messageConverter?=?messageConverter;??
28. ????}??
29. ???
30. }??
???????當我們使用MessageListenerAdapter來作為消息監聽器的時候,我們可以為它指定一個對應的MessageConverter,這樣Spring在處理接收到的消息的時候就會自動地利用我們指定的MessageConverter對它進行轉換,然后把轉換后的Java對象作為參數調用指定的消息處理方法。這里我們再把前面講解MessageListenerAdapter時定義的MessageListenerAdapter拿來做一個測試,我們指定它的MessageConverter為我們定義好的EmailMessageConverter。
Xml代碼??
1. ??
2. bean?id="messageListenerAdapter"?class="org.springframework.jms.listener.adapter.MessageListenerAdapter">??
3. ????property?name="delegate">??
4. ????????bean?class="com.tiantian.springintejms.listener.ConsumerListener"/>??
5. ????property>??
6. ????property?name="defaultListenerMethod"?value="receiveMessage"/>??
7. ????property?name="messageConverter"?ref="emailMessageConverter"/>??
8. bean>??
9. ??
10. bean?id="messageListenerAdapterContainer"?class="org.springframework.jms.listener.DefaultMessageListenerContainer">??
11. ????property?name="connectionFactory"?ref="connectionFactory"/>??
12. ????property?name="destination"?ref="adapterQueue"/>??
13. ????property?name="messageListener"?ref="messageListenerAdapter"/>??
14. bean>??
???????然后在我們的真正用于處理接收到的消息的ConsumerListener中添加一個receiveMessage方法,添加后其代碼如下所示:
Java代碼??
1. public?class?ConsumerListener?{??
2. ???
3. ????public?void?receiveMessage(String?message)?{??
4. ????????System.out.println("ConsumerListener通過receiveMessage接收到一個純文本消息,消息內容是:"?+?message);??
5. ????}??
6. ??????
7. ????public?void?receiveMessage(Email?email)?{??
8. ????????System.out.println("接收到一個包含Email的ObjectMessage。");??
9. ????????System.out.println(email);??
10. ????}??
11. ??????
12. }??
???????然后我們定義如下測試代碼:
Java代碼??
1. @Test??
2. public?void?testObjectMessage()?{??
3. ????Email?email?=?new?Email("zhangsan@xxx.com",?"主題",?"內容");??
4. ????producerService.sendMessage(adapterQueue,?email);??
5. }??
???????因為我們給MessageListenerAdapter指定了一個MessageConverter,而且是一個EmailMessageConverter,所以當MessageListenerAdapter接收到一個消息后,它會調用我們指定的MessageConverter的fromMessage方法把它轉換成一個Java對象,根據定義這里會轉換成一個Email對象,然后會把這個Email對象作為參數調用我們通過defaultListenerMethod屬性指定的默認處理器方法,根據定義這里就是receiveMessage方法,但是我們可以看到在ConsumerListener中我們一共定義了兩個receiveMessage方法,因為是通過轉換后的Email對象作為參數進行方法調用的,所以這里調用的就應該是參數類型為Email的receiveMessage方法了。上述測試代碼運行后會輸出如下結果:

????????說到這里可能有讀者就會有疑問了,說我們在之前講解MessageListenerAdapter的時候不是沒有指定對應的MessageConverter,然后發送了一個TextMessage,結果Spring還是把它轉換成一個String對象,調用了ConsumerListener參數類型為String的receiveMessage方法嗎?那你這個MessageConverter在MessageListenerAdapter進行消息接收的時候也沒什么用啊。
???????其實還是有用的,在我們使用MessageListenerAdapter時,在對其進行初始化也就是調用其構造方法時,它會默認new一個Spring已經為我們實現了的MessageConverter——SimpleMessageConverter作為其默認的MessageConverter,這也就是為什么我們在使用MessageListenerAdapter的時候不需要指定MessageConverter但是消息還是會轉換成對應的Java對象的原因。所以默認情況下我們使用MessageListenerAdapter時其對應的MessageListener的處理器方法參數類型必須是一個普通Java對象,而不能是對應的Jms Message對象。
???????那如果我們在處理Jms Message的時候想使用MessageListenerAdapter,然后又希望處理最原始的Message,而不是經過MessageConverter進行轉換后的Message該怎么辦呢?這個時候我們只需要在定義MessageListenerAdapter的時候指定其MessageConverter為空就可以了。
Xml代碼??
1. ??
2. bean?id="messageListenerAdapter"?class="org.springframework.jms.listener.adapter.MessageListenerAdapter">??
3. ????property?name="delegate">??
4. ????????bean?class="com.tiantian.springintejms.listener.ConsumerListener"/>??
5. ????property>??
6. ????property?name="defaultListenerMethod"?value="receiveMessage"/>??
7. ????property?name="messageConverter">??
8. ????????null/>??
9. ????property>??
10. bean>??
???????那么這個時候我們的真實MessageListener的處理器方法參數類型就應該是Jms Message或對應的Jms Message子類型了,不然就會調用不到對應的處理方法了。這里因為我們發送的是一個ObjectMessage,所以這里就添加一個對應的參數類型為ObjectMessage的receiveMessage方法了。
Java代碼??
1. public?void?receiveMessage(ObjectMessage?message)?throws?JMSException?{??
2. ????System.out.println(message.getObject());??
3. }??
???????剛剛講到Spring已經為我們實現了一個簡單的MessageConverter,即org.springframework.jms.support.converter.SimpleMessageConverter,其實Spring在初始化JmsTemplate的時候也指定了其對應的MessageConverter為一個SimpleMessageConverter,所以如果我們平常沒有什么特殊要求的時候可以直接使用JmsTemplate的convertAndSend系列方法進行消息發送,而不必繁瑣的在調用send方法時自己new一個MessageCreator進行相應Message的創建。
這里我們也來看一下SimpleMessageConverter的定義,如果覺得它不能滿足你的要求,那我們可以對它里面的部分方法進行重寫,或者是完全實現自己的MessageConverter。
Java代碼??
1. public?class?SimpleMessageConverter?implements?MessageConverter?{??
2. ???
3. ????public?Message?toMessage(Object?object,?Session?session)?throws?JMSException,?MessageConversionException?{??
4. ????????if?(object?instanceof?Message)?{??
5. ????????????return?(Message)?object;??
6. ????????}??
7. ????????else?if?(object?instanceof?String)?{??
8. ????????????return?createMessageForString((String)?object,?session);??
9. ????????}??
10. ????????else?if?(object?instanceof?byte[])?{??
11. ????????????return?createMessageForByteArray((byte[])?object,?session);??
12. ????????}??
13. ????????else?if?(object?instanceof?Map)?{??
14. ????????????return?createMessageForMap((Map)?object,?session);??
15. ????????}??
16. ????????else?if?(object?instanceof?Serializable)?{??
17. ????????????return?createMessageForSerializable(((Serializable)?object),?session);??
18. ????????}??
19. ??
20. ????????else?{??
21. ????????????throw?new?MessageConversionException("Cannot?convert?object?of?type?["?+??
22. ????????????????????ObjectUtils.nullSafeClassName(object)?+?"]?to?JMS?message.?Supported?message?"?+??
23. ????????????????????"payloads?are:?String,?byte?array,?Map,?Serializable?object.");??
24. ????????}??
25. ????}??
26. ???
27. ????public?Object?fromMessage(Message?message)?throws?JMSException,?MessageConversionException?{??
28. ????????if?(message?instanceof?TextMessage)?{??
29. ????????????return?extractStringFromMessage((TextMessage)?message);??
30. ????????}??
31. ????????else?if?(message?instanceof?BytesMessage)?{??
32. ????????????return?extractByteArrayFromMessage((BytesMessage)?message);??
33. ????????}??
34. ????????else?if?(message?instanceof?MapMessage)?{??
35. ????????????return?extractMapFromMessage((MapMessage)?message);??
36. ????????}??
37. ????????else?if?(message?instanceof?ObjectMessage)?{??
38. ????????????return?extractSerializableFromMessage((ObjectMessage)?message);??
39. ????????}??
40. ????????else?{??
41. ????????????return?message;??
42. ????????}??
43. ????}??
44. ???
45. ????protected?TextMessage?createMessageForString(String?text,?Session?session)?throws?JMSException?{??
46. ????????return?session.createTextMessage(text);??
47. ????}??
48. ???
49. ????protected?BytesMessage?createMessageForByteArray(byte[]?bytes,?Session?session)?throws?JMSException?{??
50. ????????BytesMessage?message?=?session.createBytesMessage();??
51. ????????message.writeBytes(bytes);??
52. ????????return?message;??
53. ????}??
54. ???
55. ????protected?MapMessage?createMessageForMap(Map?map,?Session?session)?throws?JMSException?{??
56. ????????MapMessage?message?=?session.createMapMessage();??
57. ????????for?(Map.Entry?entry?:?map.entrySet())?{??
58. ????????????if?(!(entry.getKey()?instanceof?String))?{??
59. ????????????????throw?new?MessageConversionException("Cannot?convert?non-String?key?of?type?["?+??
60. ????????????????????????ObjectUtils.nullSafeClassName(entry.getKey())?+?"]?to?JMS?MapMessage?entry");??
61. ????????????}??
62. ????????????message.setObject((String)?entry.getKey(),?entry.getValue());??
63. ????????}??
64. ????????return?message;??
65. ????}??
66. ???
67. ????protected?ObjectMessage?createMessageForSerializable(Serializable?object,?Session?session)?throws?JMSException?{??
68. ????????return?session.createObjectMessage(object);??
69. ????}??
70. ???
71. ???
72. ????protected?String?extractStringFromMessage(TextMessage?message)?throws?JMSException?{??
73. ????????return?message.getText();??
74. ????}??
75. ???
76. ????protected?byte[]?extractByteArrayFromMessage(BytesMessage?message)?throws?JMSException?{??
77. ????????byte[]?bytes?=?new?byte[(int)?message.getBodyLength()];??
78. ????????message.readBytes(bytes);??
79. ????????return?bytes;??
80. ????}??
81. ???
82. ????protected?Map?extractMapFromMessage(MapMessage?message)?throws?JMSException?{??
83. ????????Map?map?=?new?HashMap();??
84. ????????Enumeration?en?=?message.getMapNames();??
85. ????????while?(en.hasMoreElements())?{??
86. ????????????String?key?=?(String)?en.nextElement();??
87. ????????????map.put(key,?message.getObject(key));??
88. ????????}??
89. ????????return?map;??
90. ????}??
91. ???
92. ????protected?Serializable?extractSerializableFromMessage(ObjectMessage?message)?throws?JMSException?{??
93. ????????return?message.getObject();??
94. ????}??
95. ???
96. } ?