*注:別人那複製來的
1.4 消息轉換器MessageConverter
MessageConverter的做用主要有兩方面,一方面它能夠把咱們的非標準化Message對象轉換成咱們的目標Message對象,這主要是用在發送消息的時候;另外一方面它又能夠把咱們的Message對象轉換成對應的目標對象,這主要是用在接收消息的時候。java
下面咱們就拿發送一個對象消息來舉例,假設咱們有這樣一個需求:咱們平臺有一個發送郵件的功能,進行發送的時候咱們只是把咱們的相關信息封裝成一個JMS消息,而後利用JMS進行發送,在對應的消息監聽器進行接收到的消息處理時才真正的進行消息發送。spring
假設咱們有這麼一個Email對象:session
- public class Email implements Serializable {
-
- private static final long serialVersionUID = -658250125732806493L;
-
- private String receiver;
- private String title;
- private String content;
-
- public Email(String receiver, String title, String content) {
- this.receiver = receiver;
- this.title = title;
- this.content = content;
- }
-
- public String getReceiver() {
- return receiver;
- }
-
- public void setReceiver(String receiver) {
- this.receiver = receiver;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this.content = content;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("Email [receiver=").append(receiver).append(", title=")
- .append(title).append(", content=").append(content).append("]");
- return builder.toString();
- }
-
- }
這個Email對象包含了一個簡單的接收者email地址、郵件主題和郵件內容。咱們在發送的時候就把這個對象封裝成一個ObjectMessage進行發送。代碼以下所示:app
- public class ProducerServiceImpl implements ProducerService {
-
- @Autowired
- private JmsTemplate jmsTemplate;
-
- public void sendMessage(Destination destination, final Serializable obj) {
- jmsTemplate.send(destination, new MessageCreator() {
-
- public Message createMessage(Session session) throws JMSException {
- ObjectMessage objMessage = session.createObjectMessage(obj);
- return objMessage;
- }
-
- });
- }
-
- }
這是對應的在沒有使用MessageConverter的時候咱們須要new一個MessageCreator接口對象,而後在其抽象方法createMessage內部使用session建立一個對應的消息。在使用了MessageConverter的時候咱們在使用JmsTemplate進行消息發送時只須要調用其對應的convertAndSend方法便可。如:ide
- public void sendMessage(Destination destination, final Serializable obj) {
- //未使用MessageConverter的狀況
- /*jmsTemplate.send(destination, new MessageCreator() {
-
- public Message createMessage(Session session) throws JMSException {
- ObjectMessage objMessage = session.createObjectMessage(obj);
- return objMessage;
- }
-
- });*/
- //使用MessageConverter的狀況
- jmsTemplate.convertAndSend(destination, obj);
- }
這樣JmsTemplate就會在其內部調用預約的MessageConverter對咱們的消息對象進行轉換,而後再進行發送。工具
這個時候咱們就須要定義咱們的MessageConverter了。要定義本身的MessageConverter很簡單,只須要實現Spring爲咱們提供的MessageConverter接口便可。咱們先來看一下MessageConverter接口的定義:測試
- public interface MessageConverter {
-
- Message toMessage(Object object, Session session) throws JMSException, MessageConversionException;
-
- Object fromMessage(Message message) throws JMSException, MessageConversionException;
-
- }
咱們能夠看到其中一共定義了兩個方法fromMessage和toMessage,fromMessage是用來把一個JMS Message轉換成對應的Java對象,而toMessage方法是用來把一個Java對象轉換成對應的JMS Message。由於咱們已經知道上面要發送的對象就是一個Email對象,因此在這裏咱們就簡單地定義一個EmailMessageConverter用來把Email對象和對應的ObjectMessage進行轉換,其代碼以下:ui
- import javax.jms.JMSException;
- import javax.jms.Message;
- import javax.jms.ObjectMessage;
- import javax.jms.Session;
-
- import org.springframework.jms.support.converter.MessageConversionException;
- import org.springframework.jms.support.converter.MessageConverter;
-
- public class EmailMessageConverter implements MessageConverter {
-
- public Message toMessage(Object object, Session session)
- throws JMSException, MessageConversionException {
- return session.createObjectMessage((Serializable) object);
- }
-
- public Object fromMessage(Message message) throws JMSException,
- MessageConversionException {
- ObjectMessage objMessage = (ObjectMessage) message;
- return objMessage.getObject();
- }
-
- }
這樣當咱們利用JmsTemplate的convertAndSend方法發送一個Email對象的時候就會把對應的Email對象當作參數調用咱們定義好的EmailMessageConverter的toMessage方法。this
定義好咱們的EmailMessageConverter以後就須要指定咱們用來發送Email對象的JmsTemplate對象的messageConverter爲EmailMessageConverter,這裏咱們在Spring的配置文件中定義JmsTemplate bean的時候就指定:xml
- <!-- Spring提供的JMS工具類,它能夠進行消息發送、接收等 -->
- <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
- <!-- 這個connectionFactory對應的是咱們定義的Spring提供的那個ConnectionFactory對象 -->
- <property name="connectionFactory" ref="connectionFactory"/>
- <!-- 消息轉換器 -->
- <property name="messageConverter" ref="emailMessageConverter"/>
- </bean>
- <!-- 類型轉換器 -->
- <bean id="emailMessageConverter" class="com.tiantian.springintejms.converter.EmailMessageConverter"/>
到此咱們的MessageConverter就定義好了,也可以進行使用了,接着咱們來進行測試一下,定義測試代碼以下所示:
- @Test
- public void testObjectMessage() {
- Email email = new Email("zhangsan@xxx.com", "主題", "內容");
- producerService.sendMessage(destination, email);
- }
上面destination對應的接收處理的MessageListener方法以下所示:
- public class ConsumerMessageListener implements MessageListener {
-
- public void onMessage(Message message) {
-
- if (message instanceof ObjectMessage) {
- ObjectMessage objMessage = (ObjectMessage) message;
- try {
- Object obj = objMessage.getObject();
- Email email = (Email) obj;
- System.out.println("接收到一個ObjectMessage,包含Email對象。");
- System.out.println(email);
- } catch (JMSException e) {
- e.printStackTrace();
- }
- }
- }
-
- }
以前說了MessageConverter有兩方面的功能,除了把Java對象轉換成對應的Jms Message以外還能夠把Jms Message轉換成對應的Java對象。咱們看上面的消息監聽器在接收消息的時候接收到的就是一個Jms Message,若是咱們要利用MessageConverter來把它轉換成對應的Java對象的話,只能是咱們往裏面注入一個對應的MessageConverter,而後在裏面手動的調用,如:
- public class ConsumerMessageListener implements MessageListener {
-
- private MessageConverter messageConverter;
-
- public void onMessage(Message message) {
-
- if (message instanceof ObjectMessage) {
- ObjectMessage objMessage = (ObjectMessage) message;
- try {
- /*Object obj = objMessage.getObject();
- Email email = (Email) obj;*/
- Email email = (Email) messageConverter.fromMessage(objMessage);
- System.out.println("接收到一個ObjectMessage,包含Email對象。");
- System.out.println(email);
- } catch (JMSException e) {
- e.printStackTrace();
- }
-
- }
- }
-
- public MessageConverter getMessageConverter() {
- return messageConverter;
- }
-
- public void setMessageConverter(MessageConverter messageConverter) {
- this.messageConverter = messageConverter;
- }
-
- }
當咱們使用MessageListenerAdapter來做爲消息監聽器的時候,咱們能夠爲它指定一個對應的MessageConverter,這樣Spring在處理接收到的消息的時候就會自動地利用咱們指定的MessageConverter對它進行轉換,而後把轉換後的Java對象做爲參數調用指定的消息處理方法。這裏咱們再把前面講解MessageListenerAdapter時定義的MessageListenerAdapter拿來作一個測試,咱們指定它的MessageConverter爲咱們定義好的EmailMessageConverter。
- <!-- 消息監聽適配器 -->
- <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
- <property name="delegate">
- <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>
- </property>
- <property name="defaultListenerMethod" value="receiveMessage"/>
- <property name="messageConverter" ref="emailMessageConverter"/>
- </bean>
- <!-- 消息監聽適配器對應的監聽容器 -->
- <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
- <property name="connectionFactory" ref="connectionFactory"/>
- <property name="destination" ref="adapterQueue"/>
- <property name="messageListener" ref="messageListenerAdapter"/><!-- 使用MessageListenerAdapter來做爲消息監聽器 -->
- </bean>
而後在咱們的真正用於處理接收到的消息的ConsumerListener中添加一個receiveMessage方法,添加後其代碼以下所示:
- public class ConsumerListener {
-
- public void receiveMessage(String message) {
- System.out.println("ConsumerListener經過receiveMessage接收到一個純文本消息,消息內容是:" + message);
- }
-
- public void receiveMessage(Email email) {
- System.out.println("接收到一個包含Email的ObjectMessage。");
- System.out.println(email);
- }
-
- }
而後咱們定義以下測試代碼:
- @Test
- public void testObjectMessage() {
- Email email = new Email("zhangsan@xxx.com", "主題", "內容");
- producerService.sendMessage(adapterQueue, email);
- }
由於咱們給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爲空就能夠了。
- <!-- 消息監聽適配器 -->
- <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
- <property name="delegate">
- <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>
- </property>
- <property name="defaultListenerMethod" value="receiveMessage"/>
- <property name="messageConverter">
- <null/>
- </property>
- </bean>
那麼這個時候咱們的真實MessageListener的處理器方法參數類型就應該是Jms Message或對應的Jms Message子類型了,否則就會調用不到對應的處理方法了。這裏由於咱們發送的是一個ObjectMessage,因此這裏就添加一個對應的參數類型爲ObjectMessage的receiveMessage方法了。
- public void receiveMessage(ObjectMessage message) throws JMSException {
- System.out.println(message.getObject());
- }
剛剛講到Spring已經爲咱們實現了一個簡單的MessageConverter,即org.springframework.jms.support.converter.SimpleMessageConverter,其實Spring在初始化JmsTemplate的時候也指定了其對應的MessageConverter爲一個SimpleMessageConverter,因此若是咱們日常沒有什麼特殊要求的時候能夠直接使用JmsTemplate的convertAndSend系列方法進行消息發送,而沒必要繁瑣的在調用send方法時本身new一個MessageCreator進行相應Message的建立。
這裏咱們也來看一下SimpleMessageConverter的定義,若是以爲它不能知足你的要求,那咱們能夠對它裏面的部分方法進行重寫,或者是徹底實現本身的MessageConverter。
- public class SimpleMessageConverter implements MessageConverter {
-
- public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
- if (object instanceof Message) {
- return (Message) object;
- }
- else if (object instanceof String) {
- return createMessageForString((String) object, session);
- }
- else if (object instanceof byte[]) {
- return createMessageForByteArray((byte[]) object, session);
- }
- else if (object instanceof Map) {
- return createMessageForMap((Map) object, session);
- }
- else if (object instanceof Serializable) {
- return createMessageForSerializable(((Serializable) object), session);
- }
-
- else {
- throw new MessageConversionException("Cannot convert object of type [" +
- ObjectUtils.nullSafeClassName(object) + "] to JMS message. Supported message " +
- "payloads are: String, byte array, Map<String,?>, Serializable object.");
- }
- }
-
- public Object fromMessage(Message message) throws JMSException, MessageConversionException {
- if (message instanceof TextMessage) {
- return extractStringFromMessage((TextMessage) message);
- }
- else if (message instanceof BytesMessage) {
- return extractByteArrayFromMessage((BytesMessage) message);
- }
- else if (message instanceof MapMessage) {
- return extractMapFromMessage((MapMessage) message);
- }
- else if (message instanceof ObjectMessage) {
- return extractSerializableFromMessage((ObjectMessage) message);
- }
- else {
- return message;
- }
- }
-
- protected TextMessage createMessageForString(String text, Session session) throws JMSException {
- return session.createTextMessage(text);
- }
-
- protected BytesMessage createMessageForByteArray(byte[] bytes, Session session) throws JMSException {
- BytesMessage message = session.createBytesMessage();
- message.writeBytes(bytes);
- return message;
- }
-
- protected MapMessage createMessageForMap(Map<?, ?> map, Session session) throws JMSException {
- MapMessage message = session.createMapMessage();
- for (Map.Entry entry : map.entrySet()) {
- if (!(entry.getKey() instanceof String)) {
- throw new MessageConversionException("Cannot convert non-String key of type [" +
- ObjectUtils.nullSafeClassName(entry.getKey()) + "] to JMS MapMessage entry");
- }
- message.setObject((String) entry.getKey(), entry.getValue());
- }
- return message;
- }
-
- protected ObjectMessage createMessageForSerializable(Serializable object, Session session) throws JMSException {
- return session.createObjectMessage(object);
- }
-
-
- protected String extractStringFromMessage(TextMessage message) throws JMSException {
- return message.getText();
- }
-
- protected byte[] extractByteArrayFromMessage(BytesMessage message) throws JMSException {
- byte[] bytes = new byte[(int) message.getBodyLength()];
- message.readBytes(bytes);
- return bytes;
- }
-
- protected Map extractMapFromMessage(MapMessage message) throws JMSException {
- Map<String, Object> map = new HashMap<String, Object>();
- Enumeration en = message.getMapNames();
- while (en.hasMoreElements()) {
- String key = (String) en.nextElement();
- map.put(key, message.getObject(key));
- }
- return map;
- }
-
- protected Serializable extractSerializableFromMessage(ObjectMessage message) throws JMSException {
- return message.getObject();
- }
-
- }
附: