Spring整合JMS(三)——MessageConverter介紹


1.4     消息轉換器MessageConverter

MessageConverter的作用主要有兩方面,一方面它可以把我們的非標准化Message對象轉換成我們的目標Message對象,這主要是用在發送消息的時候;另一方面它又可以把我們的Message對象轉換成對應的目標對象,這主要是用在接收消息的時候。

下面我們就拿發送一個對象消息來舉例,假設我們有這樣一個需求:我們平台有一個發送郵件的功能,進行發送的時候我們只是把我們的相關信息封裝成一個JMS消息,然后利用JMS進行發送,在對應的消息監聽器進行接收到的消息處理時才真正的進行消息發送。

假設我們有這么一個Email對象:

 

Java代碼  

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代碼  

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代碼  

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代碼  

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;  

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工具類,它可以進行消息發送、接收等 -->  

<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  

public void testObjectMessage() {  

    Email email = new Email("zhangsan@xxx.com", "主題", "內容");  

    producerService.sendMessage(destination, email);  

}  

 

       上面destination對應的接收處理的MessageListener方法如下所示:

 

Java代碼  

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代碼  

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代碼  

<!-- 消息監聽適配器 -->  

<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代碼  

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  

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為空就可以了。

 

Xml代碼  

<!-- 消息監聽適配器 -->  

<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代碼  
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代碼  

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();  

    }  

}

轉載自

Elim的博客

http://elim.iteye.com/blog/1900937


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM