1.4 ???? 消息轉(zhuǎn)換器 MessageConverter
MessageConverter 的作用主要有兩方面,一方面它可以把我們的非標準化 Message 對象轉(zhuǎn)換成我們的目標 Message 對象,這主要是用在發(fā)送消息的時候;另一方面它又可以把我們的 Message 對象轉(zhuǎn)換成對應(yīng)的目標對象,這主要是用在接收消息的時候。
下面我們就拿發(fā)送一個對象消息來舉例,假設(shè)我們有這樣一個需求:我們平臺有一個發(fā)送郵件的功能,進行發(fā)送的時候我們只是把我們的相關(guān)信息封裝成一個 JMS 消息,然后利用 JMS 進行發(fā)送,在對應(yīng)的消息監(jiān)聽器進行接收到的消息處理時才真正的進行消息發(fā)送。
假設(shè)我們有這么一個 Email 對象:
?
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 地址、郵件主題和郵件內(nèi)容。我們在發(fā)送的時候就把這個對象封裝成一個 ObjectMessage 進行發(fā)送。代碼如下所示:
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; } }); } }
?????? 這是對應(yīng)的在沒有使用 MessageConverter 的時候我們需要 new 一個 MessageCreator 接口對象,然后在其抽象方法 createMessage 內(nèi)部使用 session 創(chuàng)建一個對應(yīng)的消息。在使用了 MessageConverter 的時候我們在使用 JmsTemplate 進行消息發(fā)送時只需要調(diào)用其對應(yīng)的 convertAndSend 方法即可。如:
?
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 就會在其內(nèi)部調(diào)用預(yù)定的 MessageConverter 對我們的消息對象進行轉(zhuǎn)換,然后再進行發(fā)送。
?????? 這個時候我們就需要定義我們的 MessageConverter 了。要定義自己的 MessageConverter 很簡單,只需要實現(xiàn) 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 轉(zhuǎn)換成對應(yīng)的 Java 對象,而 toMessage 方法是用來把一個 Java 對象轉(zhuǎn)換成對應(yīng)的 JMS Message 。因為我們已經(jīng)知道上面要發(fā)送的對象就是一個 Email 對象,所以在這里我們就簡單地定義一個 EmailMessageConverter 用來把 Email 對象和對應(yīng)的 ObjectMessage 進行轉(zhuǎn)換,其代碼如下:
?
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 方法發(fā)送一個 Email 對象的時候就會把對應(yīng)的 Email 對象當做參數(shù)調(diào)用我們定義好的 EmailMessageConverter 的 toMessage 方法。
?????? 定義好我們的 EmailMessageConverter 之后就需要指定我們用來發(fā)送 Email 對象的 JmsTemplate 對象的 messageConverter 為 EmailMessageConverter ,這里我們在 Spring 的配置文件中定義 JmsTemplate bean 的時候就指定:
?
<!-- Spring提供的JMS工具類,它可以進行消息發(fā)送、接收等 --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <!-- 這個connectionFactory對應(yīng)的是我們定義的Spring提供的那個ConnectionFactory對象 --> <property name="connectionFactory" ref="connectionFactory"/> <!-- 消息轉(zhuǎn)換器 --> <property name="messageConverter" ref="emailMessageConverter"/> </bean> <!-- 類型轉(zhuǎn)換器 --> <bean id="emailMessageConverter" class="com.tiantian.springintejms.converter.EmailMessageConverter"/>
????
?????? 到此我們的 MessageConverter 就定義好了,也能夠進行使用了,接著我們來進行測試一下,定義測試代碼如下所示:
?
@Test public void testObjectMessage() { Email email = new Email("zhangsan@xxx.com", "主題", "內(nèi)容"); producerService.sendMessage(destination, email); }
?
?????? 上面 destination 對應(yīng)的接收處理的 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 對象轉(zhuǎn)換成對應(yīng)的 Jms Message 之外還可以把 Jms Message 轉(zhuǎn)換成對應(yīng)的 Java 對象。我們看上面的消息監(jiān)聽器在接收消息的時候接收到的就是一個 Jms Message ,如果我們要利用 MessageConverter 來把它轉(zhuǎn)換成對應(yīng)的 Java 對象的話,只能是我們往里面注入一個對應(yīng)的 MessageConverter ,然后在里面手動的調(diào)用,如:
?
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 來作為消息監(jiān)聽器的時候,我們可以為它指定一個對應(yīng)的 MessageConverter ,這樣 Spring 在處理接收到的消息的時候就會自動地利用我們指定的 MessageConverter 對它進行轉(zhuǎn)換,然后把轉(zhuǎn)換后的 Java 對象作為參數(shù)調(diào)用指定的消息處理方法。這里我們再把前面講解 MessageListenerAdapter 時定義的 MessageListenerAdapter 拿來做一個測試,我們指定它的 MessageConverter 為我們定義好的 EmailMessageConverter 。
?
<!-- 消息監(jiān)聽適配器 --> <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> <!-- 消息監(jiān)聽適配器對應(yīng)的監(jiān)聽容器 --> <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來作為消息監(jiān)聽器 --> </bean>
?
?????? 然后在我們的真正用于處理接收到的消息的 ConsumerListener 中添加一個 receiveMessage 方法,添加后其代碼如下所示:
?
public class ConsumerListener { public void receiveMessage(String message) { System.out.println("ConsumerListener通過receiveMessage接收到一個純文本消息,消息內(nèi)容是:" + 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", "主題", "內(nèi)容"); producerService.sendMessage(adapterQueue, email); }
?
??????
因為我們給
MessageListenerAdapter
指定了一個
MessageConverter
,而且是一個
EmailMessageConverter
,所以當
MessageListenerAdapter
接收到一個消息后,它會調(diào)用我們指定的
MessageConverter
的
fromMessage
方法把它轉(zhuǎn)換成一個
Java
對象,根據(jù)定義這里會轉(zhuǎn)換成一個
Email
對象,然后會把這個
Email
對象作為參數(shù)調(diào)用我們通過
defaultListenerMethod
屬性指定的默認處理器方法,根據(jù)定義這里就是
receiveMessage
方法,但是我們可以看到在
ConsumerListener
中我們一共定義了兩個
receiveMessage
方法,因為是通過轉(zhuǎn)換后的
Email
對象作為參數(shù)進行方法調(diào)用的,所以這里調(diào)用的就應(yīng)該是參數(shù)類型為
Email
的
receiveMessage
方法了。上述測試代碼運行后會輸出如下結(jié)果:
???????
說到這里可能有讀者就會有疑問了,說我們在之前講解
MessageListenerAdapter
的時候不是沒有指定對應(yīng)的
MessageConverter
,然后發(fā)送了一個
TextMessage
,結(jié)果
Spring
還是把它轉(zhuǎn)換成一個
String
對象,調(diào)用了
ConsumerListener
參數(shù)類型為
String
的
receiveMessage
方法嗎?那你這個
MessageConverter
在
MessageListenerAdapter
進行消息接收的時候也沒什么用啊。
?????? 其實還是有用的,在我們使用 MessageListenerAdapter 時,在對其進行初始化也就是調(diào)用其構(gòu)造方法時,它會默認 new 一個 Spring 已經(jīng)為我們實現(xiàn)了的 MessageConverter —— SimpleMessageConverter 作為其默認的 MessageConverter ,這也就是為什么我們在使用 MessageListenerAdapter 的時候不需要指定 MessageConverter 但是消息還是會轉(zhuǎn)換成對應(yīng)的 Java 對象的原因。所以默認情況下我們使用 MessageListenerAdapter 時其對應(yīng)的 MessageListener 的處理器方法參數(shù)類型必須是一個普通 Java 對象,而不能是對應(yīng)的 Jms Message 對象。
?????? 那如果我們在處理 Jms Message 的時候想使用 MessageListenerAdapter ,然后又希望處理最原始的 Message ,而不是經(jīng)過 MessageConverter 進行轉(zhuǎn)換后的 Message 該怎么辦呢?這個時候我們只需要在定義 MessageListenerAdapter 的時候指定其 MessageConverter 為空就可以了。
?
<!-- 消息監(jiān)聽適配器 --> <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 的處理器方法參數(shù)類型就應(yīng)該是 Jms Message 或?qū)?yīng)的 Jms Message 子類型了,不然就會調(diào)用不到對應(yīng)的處理方法了。這里因為我們發(fā)送的是一個 ObjectMessage ,所以這里就添加一個對應(yīng)的參數(shù)類型為 ObjectMessage 的 receiveMessage 方法了。
?
public void receiveMessage(ObjectMessage message) throws JMSException { System.out.println(message.getObject()); }
?
?????? 剛剛講到 Spring 已經(jīng)為我們實現(xiàn)了一個簡單的 MessageConverter ,即 org.springframework.jms.support.converter.SimpleMessageConverter ,其實 Spring 在初始化 JmsTemplate 的時候也指定了其對應(yīng)的 MessageConverter 為一個 SimpleMessageConverter ,所以如果我們平常沒有什么特殊要求的時候可以直接使用 JmsTemplate 的 convertAndSend 系列方法進行消息發(fā)送,而不必繁瑣的在調(diào)用 send 方法時自己 new 一個 MessageCreator 進行相應(yīng) Message 的創(chuàng)建。
這里我們也來看一下 SimpleMessageConverter 的定義,如果覺得它不能滿足你的要求,那我們可以對它里面的部分方法進行重寫,或者是完全實現(xiàn)自己的 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(); } }
附:
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
