mq訊息轉換器MessageConverter
1.4訊息轉換器MessageConverter
MessageConverter的作用主要有兩方面,一方面它可以把我們的非標準化Message物件轉換成我們的目標Message物件,這主要是用在傳送訊息的時候;另一方面它又可以把我們的Message物件轉換成對應的目標物件,這主要是用在接收訊息的時候。
下面我們就拿傳送一個物件訊息來舉例,假設我們有這樣一個需求:我們平臺有一個傳送郵件的功能,進行傳送的時候我們只是把我們的相關資訊封裝成一個JMS訊息,然後利用JMS進行傳送,在對應的訊息監聽器進行接收到的訊息處理時才真正的進行訊息傳送。
假設我們有這麼一個Email物件:
Java程式碼- publicclass Email implements Serializable {
- privatestaticfinallong 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;
- }
- publicvoid setReceiver(String receiver) {
- this.receiver = receiver;
- }
- public String getTitle() {
- return title;
- }
- publicvoid
- this.title = title;
- }
- public String getContent() {
- return content;
- }
- publicvoid 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();
- }
- }
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進行傳送。程式碼如下所示:
Java程式碼- publicclass ProducerServiceImpl implements ProducerService {
- @Autowired
- private JmsTemplate jmsTemplate;
- publicvoid 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;
- }
- });
- }
- }
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方法即可。如:
Java程式碼- publicvoid 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);
- }
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介面的定義:
Java程式碼- publicinterface MessageConverter {
- Message toMessage(Object object, Session session) throws JMSException, MessageConversionException;
- Object fromMessage(Message message) throws JMSException, MessageConversionException;
- }
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進行轉換,其程式碼如下:
Java程式碼- 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;
- publicclass 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();
- }
- }
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方法。
定義好我們的EmailMessageConverter之後就需要指定我們用來發送Email物件的JmsTemplate物件的messageConverter為EmailMessageConverter,這裡我們在Spring的配置檔案中定義JmsTemplate bean的時候就指定:
Xml程式碼- <!– Spring提供的JMS工具類,它可以進行訊息傳送、接收等 –>
- <beanid=“jmsTemplate”class=“org.springframework.jms.core.JmsTemplate”>
- <!– 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory物件 –>
- <propertyname=“connectionFactory”ref=“connectionFactory”/>
- <!– 訊息轉換器 –>
- <propertyname=“messageConverter”ref=“emailMessageConverter”/>
- </bean>
- <!– 型別轉換器 –>
- <beanid=“emailMessageConverter”class=“com.tiantian.springintejms.converter.EmailMessageConverter”/>
<!-- 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就定義好了,也能夠進行使用了,接著我們來進行測試一下,定義測試程式碼如下所示:
Java程式碼- @Test
- publicvoid testObjectMessage() {
- Email email = new Email(“[email protected]”, “主題”, “內容”);
- producerService.sendMessage(destination, email);
- }
@Test
public void testObjectMessage() {
Email email = new Email("[email protected]", "主題", "內容");
producerService.sendMessage(destination, email);
}
上面destination對應的接收處理的MessageListener方法如下所示:
Java程式碼- publicclass ConsumerMessageListener implements MessageListener {
- publicvoid 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();
- }
- }
- }
- }
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,然後在裡面手動的呼叫,如:
Java程式碼- publicclass ConsumerMessageListener implements MessageListener {
- private MessageConverter messageConverter;
- publicvoid 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;
- }
- publicvoid setMessageConverter(MessageConverter messageConverter) {
- this.messageConverter = 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。
Xml程式碼- <!– 訊息監聽介面卡 –>
- <beanid=“messageListenerAdapter”class=“org.springframework.jms.listener.adapter.MessageListenerAdapter”>
- <propertyname=“delegate”>
- <beanclass=“com.tiantian.springintejms.listener.ConsumerListener”/>
- </property>
- <propertyname=“defaultListenerMethod”value=“receiveMessage”/>
- <propertyname=“messageConverter”ref=“emailMessageConverter”/>
- </bean>
- <!– 訊息監聽介面卡對應的監聽容器 –>
- <beanid=“messageListenerAdapterContainer”class=“org.springframework.jms.listener.DefaultMessageListenerContainer”>
- <propertyname=“connectionFactory”ref=“connectionFactory”/>
- <propertyname=“destination”ref=“adapterQueue”/>
- <propertyname=“messageListener”ref=“messageListenerAdapter”/><!– 使用MessageListenerAdapter來作為訊息監聽器 –>
- </bean>
<!-- 訊息監聽介面卡 --> <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方法,新增後其程式碼如下所示:
Java程式碼- publicclass ConsumerListener {
- publicvoid receiveMessage(String message) {
- System.out.println(”ConsumerListener通過receiveMessage接收到一個純文字訊息,訊息內容是:” + message);
- }
- publicvoid receiveMessage(Email email) {
- System.out.println(”接收到一個包含Email的ObjectMessage。”);
- System.out.println(email);
- }
- }
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);
}
}
然後我們定義如下測試程式碼:
Java程式碼- @Test
- publicvoid testObjectMessage() {
- Email email = new Email(“[email protected]”, “主題”, “內容”);
- producerService.sendMessage(adapterQueue, email);
- }
@Test
public void testObjectMessage() {
Email email = new Email("[email protected]", "主題", "內容");
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為空就可以了。
Xml程式碼- <!– 訊息監聽介面卡 –>
- <beanid=“messageListenerAdapter”class=“org.springframework.jms.listener.adapter.MessageListenerAdapter”>
- <propertyname=“delegate”>
- <beanclass=“com.tiantian.springintejms.listener.ConsumerListener”/>
- </property>
- <propertyname=“defaultListenerMethod”value=“receiveMessage”/>
- <propertyname=“messageConverter”>
- <null/>
- </property>
- </bean>
<!-- 訊息監聽介面卡 --> <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方法了。
Java程式碼- publicvoid receiveMessage(ObjectMessage message) throws JMSException {
- System.out.println(message.getObject());
- }
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。
Java程式碼- publicclass SimpleMessageConverter implements MessageConverter {
- public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
- if (object instanceof Message) {
- return (Message) object;
- }
- elseif (object instanceof String) {
- return createMessageForString((String) object, session);
- }
- elseif (object instanceofbyte[]) {
- return createMessageForByteArray((byte[]) object, session);
- }
- elseif (object instanceof Map) {
- return createMessageForMap((Map) object, session);
- }
- elseif (object instanceof Serializable) {
- return createMessageForSerializable(((Serializable) object), session);
- }
- else {
- thrownew 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);
- }
- elseif (message instanceof BytesMessage) {
- return extractByteArrayFromMessage((BytesMessage) message);
- }
- elseif (message instanceof MapMessage) {
- return extractMapFromMessage((MapMessage) message);
- }
- elseif (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)) {
- thrownew 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();
- }
- protectedbyte[] extractByteArrayFromMessage(BytesMessage message) throws JMSException {
- byte[] bytes = newbyte[(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();
- }
- }
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();