天天看點

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

<code>1.4</code>     <code>消息轉換器MessageConverter</code>

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

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

<code>假設我們有這麼一個Email對象:</code>

<code>         </code> 

<code>Java代碼  收藏代碼</code>

<code>    </code><code>public</code> <code>class</code> <code>Email </code><code>implements</code> <code>Serializable {</code>

<code>               </code> 

<code>        </code><code>private</code> <code>static</code> <code>final</code> <code>long</code> <code>serialVersionUID = -658250125732806493L;</code>

<code>        </code><code>private</code> <code>String receiver;</code>

<code>        </code><code>private</code> <code>String title;</code>

<code>        </code><code>private</code> <code>String content;</code>

<code>        </code><code>public</code> <code>Email(String receiver, String title, String content) {</code>

<code>            </code><code>this</code><code>.receiver = receiver;</code>

<code>            </code><code>this</code><code>.title = title;</code>

<code>            </code><code>this</code><code>.content = content;</code>

<code>        </code><code>}</code>

<code>        </code><code>public</code> <code>String getReceiver() {</code>

<code>            </code><code>return</code> <code>receiver;</code>

<code>        </code><code>public</code> <code>void</code> <code>setReceiver(String receiver) {</code>

<code>        </code><code>public</code> <code>String getTitle() {</code>

<code>            </code><code>return</code> <code>title;</code>

<code>        </code><code>public</code> <code>void</code> <code>setTitle(String title) {</code>

<code>        </code><code>public</code> <code>String getContent() {</code>

<code>            </code><code>return</code> <code>content;</code>

<code>        </code><code>public</code> <code>void</code> <code>setContent(String content) {</code>

<code>        </code><code>@Override</code>

<code>        </code><code>public</code> <code>String toString() {</code>

<code>            </code><code>StringBuilder builder = </code><code>new</code> <code>StringBuilder();</code>

<code>            </code><code>builder.append(</code><code>"Email [receiver="</code><code>).append(receiver).append(</code><code>", title="</code><code>)</code>

<code>                    </code><code>.append(title).append(</code><code>", content="</code><code>).append(content).append(</code><code>"]"</code><code>);</code>

<code>            </code><code>return</code> <code>builder.toString();</code>

<code>                  </code> 

<code>    </code><code>}</code>

<code>       </code><code>這個Email對象包含了一個簡單的接收者email位址、郵件主題和郵件内容。我們在發送的時候就把這個對象封裝成一個ObjectMessage進行發送。代碼如下所示:</code>

<code>    </code><code>public</code> <code>class</code> <code>ProducerServiceImpl </code><code>implements</code> <code>ProducerService {</code>

<code>        </code><code>@Autowired</code>

<code>        </code><code>private</code> <code>JmsTemplate jmsTemplate;    </code>

<code>              </code> 

<code>        </code><code>public</code> <code>void</code> <code>sendMessage(Destination destination, </code><code>final</code> <code>Serializable obj) {</code>

<code>            </code><code>jmsTemplate.send(destination, </code><code>new</code> <code>MessageCreator() {</code>

<code>                </code><code>public</code> <code>Message createMessage(Session session) </code><code>throws</code> <code>JMSException {</code>

<code>                    </code><code>ObjectMessage objMessage = session.createObjectMessage(obj);</code>

<code>                    </code><code>return</code> <code>objMessage;</code>

<code>                </code><code>}</code>

<code>                          </code> 

<code>            </code><code>});</code>

<code>       </code><code>這是對應的在沒有使用MessageConverter的時候我們需要</code><code>new</code><code>一個MessageCreator接口對象,然後在其抽象方法createMessage内部使用session建立一個對應的消息。在使用了MessageConverter的時候我們在使用JmsTemplate進行消息發送時隻需要調用其對應的convertAndSend方法即可。如:</code>

<code>    </code><code>public</code> <code>void</code> <code>sendMessage(Destination destination, </code><code>final</code> <code>Serializable obj) {</code>

<code>        </code><code>//未使用MessageConverter的情況</code>

<code>        </code><code>/*jmsTemplate.send(destination, new MessageCreator() {</code>

<code>             </code> 

<code>            </code><code>public Message createMessage(Session session) throws JMSException {</code>

<code>                </code><code>ObjectMessage objMessage = session.createObjectMessage(obj);</code>

<code>                </code><code>return objMessage;</code>

<code>            </code><code>}</code>

<code>                     </code> 

<code>        </code><code>});*/</code>

<code>        </code><code>//使用MessageConverter的情況</code>

<code>        </code><code>jmsTemplate.convertAndSend(destination, obj);</code>

<code>這樣JmsTemplate就會在其内部調用預定的MessageConverter對我們的消息對象進行轉換,然後再進行發送。</code>

<code>       </code><code>這個時候我們就需要定義我們的MessageConverter了。要定義自己的MessageConverter很簡單,隻需要實作Spring為我們提供的MessageConverter接口即可。我們先來看一下MessageConverter接口的定義:</code>

<code>    </code><code>public interface MessageConverter {</code>

<code>        </code><code>Message toMessage(Object object, Session session) throws JMSException, MessageConversionException;</code>

<code>        </code><code>Object fromMessage(Message message) throws JMSException, MessageConversionException;</code>

<code>       </code><code>我們可以看到其中一共定義了兩個方法fromMessage和toMessage,fromMessage是用來把一個JMS Message轉換成對應的Java對象,而toMessage方法是用來把一個Java對象轉換成對應的JMS Message。因為我們已經知道上面要發送的對象就是一個Email對象,是以在這裡我們就簡單地定義一個EmailMessageConverter用來把Email對象和對應的ObjectMessage進行轉換,其代碼如下:</code>

<code>    </code><code>import javax.jms.JMSException;</code>

<code>    </code><code>import javax.jms.Message;</code>

<code>    </code><code>import javax.jms.ObjectMessage;</code>

<code>    </code><code>import javax.jms.Session;</code>

<code>    </code><code>import org.springframework.jms.support.converter.MessageConversionException;</code>

<code>    </code><code>import org.springframework.jms.support.converter.MessageConverter;</code>

<code>    </code><code>public class EmailMessageConverter implements MessageConverter {</code>

<code>        </code><code>public Message toMessage(Object object, Session session)</code>

<code>                </code><code>throws JMSException, MessageConversionException {</code>

<code>            </code><code>return session.createObjectMessage((Serializable) object);</code>

<code>        </code><code>public Object fromMessage(Message message) throws JMSException,</code>

<code>                </code><code>MessageConversionException {</code>

<code>            </code><code>ObjectMessage objMessage = (ObjectMessage) message;</code>

<code>            </code><code>return objMessage.getObject();</code>

<code>       </code><code>這樣當我們利用JmsTemplate的convertAndSend方法發送一個Email對象的時候就會把對應的Email對象當做參數調用我們定義好的EmailMessageConverter的toMessage方法。</code>

<code>       </code><code>定義好我們的EmailMessageConverter之後就需要指定我們用來發送Email對象的JmsTemplate對象的messageConverter為EmailMessageConverter,這裡我們在Spring的配置檔案中定義JmsTemplate bean的時候就指定:</code>

<code>Xml代碼  收藏代碼</code>

<code>    </code><code>&lt;!-- Spring提供的JMS工具類,它可以進行消息發送、接收等 --&gt;</code>

<code>    </code><code>&lt;bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"&gt;</code>

<code>        </code><code>&lt;!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory對象 --&gt;</code>

<code>        </code><code>&lt;property name="connectionFactory" ref="connectionFactory"/&gt;</code>

<code>        </code><code>&lt;!-- 消息轉換器 --&gt;</code>

<code>        </code><code>&lt;property name="messageConverter" ref="emailMessageConverter"/&gt;</code>

<code>    </code><code>&lt;/bean&gt;</code>

<code>    </code><code>&lt;!-- 類型轉換器 --&gt;</code>

<code>    </code><code>&lt;bean id="emailMessageConverter" class="com.tiantian.springintejms.converter.EmailMessageConverter"/&gt;</code>

<code>            </code> 

<code>       </code><code>到此我們的MessageConverter就定義好了,也能夠進行使用了,接着我們來進行測試一下,定義測試代碼如下所示:</code>

<code>    </code><code>@Test</code>

<code>    </code><code>public void testObjectMessage() {</code>

<code>        </code><code>Email email = new Email("[email protected]", "主題", "内容");</code>

<code>        </code><code>producerService.sendMessage(destination, email);</code>

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

<code>    </code><code>public class ConsumerMessageListener implements MessageListener {</code>

<code>        </code><code>public void onMessage(Message message) {</code>

<code>                      </code> 

<code>            </code><code>if (message instanceof ObjectMessage) {</code>

<code>                </code><code>ObjectMessage objMessage = (ObjectMessage) message;</code>

<code>                </code><code>try {</code>

<code>                    </code><code>Object obj = objMessage.getObject();</code>

<code>                    </code><code>Email email = (Email) obj;</code>

<code>                    </code><code>System.out.println("接收到一個ObjectMessage,包含Email對象。");</code>

<code>                    </code><code>System.out.println(email);</code>

<code>                </code><code>} catch (JMSException e) {</code>

<code>                    </code><code>e.printStackTrace();</code>

<code>       </code><code>之前說了MessageConverter有兩方面的功能,除了把Java對象轉換成對應的Jms Message之外還可以把Jms Message轉換成對應的Java對象。我們看上面的消息監聽器在接收消息的時候接收到的就是一個Jms Message,如果我們要利用MessageConverter來把它轉換成對應的Java對象的話,隻能是我們往裡面注入一個對應的MessageConverter,然後在裡面手動的調用,如:</code>

<code>        </code><code>private MessageConverter messageConverter;</code>

<code>                    </code><code>/*Object obj = objMessage.getObject();</code>

<code>                    </code><code>Email email = (Email) obj;*/</code>

<code>                    </code><code>Email email = (Email) messageConverter.fromMessage(objMessage);</code>

<code>                    </code><code>System.out.println(</code><code>"接收到一個ObjectMessage,包含Email對象。"</code><code>);</code>

<code>                </code><code>} </code><code>catch</code> <code>(JMSException e) {</code>

<code>        </code><code>public</code> <code>MessageConverter getMessageConverter() {</code>

<code>            </code><code>return</code> <code>messageConverter;</code>

<code>        </code><code>public</code> <code>void</code> <code>setMessageConverter(MessageConverter messageConverter) {</code>

<code>            </code><code>this</code><code>.messageConverter = messageConverter;</code>

<code>       </code><code>當我們使用MessageListenerAdapter來作為消息監聽器的時候,我們可以為它指定一個對應的MessageConverter,這樣Spring在處理接收到的消息的時候就會自動地利用我們指定的MessageConverter對它進行轉換,然後把轉換後的Java對象作為參數調用指定的消息處理方法。這裡我們再把前面講解MessageListenerAdapter時定義的MessageListenerAdapter拿來做一個測試,我們指定它的MessageConverter為我們定義好的EmailMessageConverter。</code>

<code>    </code><code>&lt;!-- 消息監聽擴充卡 --&gt;</code>

<code>    </code><code>&lt;bean id=</code><code>"messageListenerAdapter"</code> <code>class</code><code>=</code><code>"org.springframework.jms.listener.adapter.MessageListenerAdapter"</code><code>&gt;</code>

<code>        </code><code>&lt;property name=</code><code>"delegate"</code><code>&gt;</code>

<code>            </code><code>&lt;bean </code><code>class</code><code>=</code><code>"com.tiantian.springintejms.listener.ConsumerListener"</code><code>/&gt;</code>

<code>        </code><code>&lt;/property&gt;</code>

<code>        </code><code>&lt;property name=</code><code>"defaultListenerMethod"</code> <code>value=</code><code>"receiveMessage"</code><code>/&gt;</code>

<code>        </code><code>&lt;property name=</code><code>"messageConverter"</code> <code>ref=</code><code>"emailMessageConverter"</code><code>/&gt;</code>

<code>    </code><code>&lt;!-- 消息監聽擴充卡對應的監聽容器 --&gt;</code>

<code>    </code><code>&lt;bean id=</code><code>"messageListenerAdapterContainer"</code> <code>class</code><code>=</code><code>"org.springframework.jms.listener.DefaultMessageListenerContainer"</code><code>&gt;</code>

<code>        </code><code>&lt;property name=</code><code>"connectionFactory"</code> <code>ref=</code><code>"connectionFactory"</code><code>/&gt;</code>

<code>        </code><code>&lt;property name=</code><code>"destination"</code> <code>ref=</code><code>"adapterQueue"</code><code>/&gt;</code>

<code>        </code><code>&lt;property name=</code><code>"messageListener"</code> <code>ref=</code><code>"messageListenerAdapter"</code><code>/&gt;&lt;!-- 使用MessageListenerAdapter來作為消息監聽器 --&gt;</code>

<code>       </code><code>然後在我們的真正用于處理接收到的消息的ConsumerListener中添加一個receiveMessage方法,添加後其代碼如下所示:</code>

<code>    </code><code>public</code> <code>class</code> <code>ConsumerListener {</code>

<code>        </code><code>public</code> <code>void</code> <code>receiveMessage(String message) {</code>

<code>            </code><code>System.out.println(</code><code>"ConsumerListener通過receiveMessage接收到一個純文字消息,消息内容是:"</code> <code>+ message);</code>

<code>        </code><code>public</code> <code>void</code> <code>receiveMessage(Email email) {</code>

<code>            </code><code>System.out.println(</code><code>"接收到一個包含Email的ObjectMessage。"</code><code>);</code>

<code>            </code><code>System.out.println(email);</code>

<code>       </code><code>然後我們定義如下測試代碼:</code>

<code>    </code><code>public</code> <code>void</code> <code>testObjectMessage() {</code>

<code>        </code><code>Email email = </code><code>new</code> <code>Email(</code><code>"[email protected]"</code><code>, </code><code>"主題"</code><code>, </code><code>"内容"</code><code>);</code>

<code>        </code><code>producerService.sendMessage(adapterQueue, email);</code>

<code>       </code><code>因為我們給MessageListenerAdapter指定了一個MessageConverter,而且是一個EmailMessageConverter,是以當MessageListenerAdapter接收到一個消息後,它會調用我們指定的MessageConverter的fromMessage方法把它轉換成一個Java對象,根據定義這裡會轉換成一個Email對象,然後會把這個Email對象作為參數調用我們通過defaultListenerMethod屬性指定的預設處理器方法,根據定義這裡就是receiveMessage方法,但是我們可以看到在ConsumerListener中我們一共定義了兩個receiveMessage方法,因為是通過轉換後的Email對象作為參數進行方法調用的,是以這裡調用的就應該是參數類型為Email的receiveMessage方法了。上述測試代碼運作後會輸出如下結果:</code>

<code>        </code><code>說到這裡可能有讀者就會有疑問了,說我們在之前講解MessageListenerAdapter的時候不是沒有指定對應的MessageConverter,然後發送了一個TextMessage,結果Spring還是把它轉換成一個String對象,調用了ConsumerListener參數類型為String的receiveMessage方法嗎?那你這個MessageConverter在MessageListenerAdapter進行消息接收的時候也沒什麼用啊。</code>

<code>       </code><code>其實還是有用的,在我們使用MessageListenerAdapter時,在對其進行初始化也就是調用其構造方法時,它會預設</code><code>new</code><code>一個Spring已經為我們實作了的MessageConverter——SimpleMessageConverter作為其預設的MessageConverter,這也就是為什麼我們在使用MessageListenerAdapter的時候不需要指定MessageConverter但是消息還是會轉換成對應的Java對象的原因。是以預設情況下我們使用MessageListenerAdapter時其對應的MessageListener的處理器方法參數類型必須是一個普通Java對象,而不能是對應的Jms Message對象。</code>

<code>       </code><code>那如果我們在處理Jms Message的時候想使用MessageListenerAdapter,然後又希望處理最原始的Message,而不是經過MessageConverter進行轉換後的Message該怎麼辦呢?這個時候我們隻需要在定義MessageListenerAdapter的時候指定其MessageConverter為空就可以了。</code>

<code>        </code><code>&lt;property name=</code><code>"messageConverter"</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>null</code><code>/&gt;</code>

<code>       </code><code>那麼這個時候我們的真實MessageListener的處理器方法參數類型就應該是Jms Message或對應的Jms Message子類型了,不然就會調用不到對應的處理方法了。這裡因為我們發送的是一個ObjectMessage,是以這裡就添加一個對應的參數類型為ObjectMessage的receiveMessage方法了。</code>

<code>    </code><code>public</code> <code>void</code> <code>receiveMessage(ObjectMessage message) </code><code>throws</code> <code>JMSException {</code>

<code>        </code><code>System.out.println(message.getObject());</code>

<code>       </code><code>剛剛講到Spring已經為我們實作了一個簡單的MessageConverter,即org.springframework.jms.support.converter.SimpleMessageConverter,其實Spring在初始化JmsTemplate的時候也指定了其對應的MessageConverter為一個SimpleMessageConverter,是以如果我們平常沒有什麼特殊要求的時候可以直接使用JmsTemplate的convertAndSend系列方法進行消息發送,而不必繁瑣的在調用send方法時自己</code><code>new</code><code>一個MessageCreator進行相應Message的建立。</code>

<code>這裡我們也來看一下SimpleMessageConverter的定義,如果覺得它不能滿足你的要求,那我們可以對它裡面的部分方法進行重寫,或者是完全實作自己的MessageConverter。</code>

<code>    </code><code>public</code> <code>class</code> <code>SimpleMessageConverter </code><code>implements</code> <code>MessageConverter {</code>

<code>        </code><code>public</code> <code>Message toMessage(Object object, Session session) </code><code>throws</code> <code>JMSException, MessageConversionException {</code>

<code>            </code><code>if</code> <code>(object </code><code>instanceof</code> <code>Message) {</code>

<code>                </code><code>return</code> <code>(Message) object;</code>

<code>            </code><code>else</code> <code>if</code> <code>(object </code><code>instanceof</code> <code>String) {</code>

<code>                </code><code>return</code> <code>createMessageForString((String) object, session);</code>

<code>            </code><code>else</code> <code>if</code> <code>(object </code><code>instanceof</code> <code>byte</code><code>[]) {</code>

<code>                </code><code>return</code> <code>createMessageForByteArray((</code><code>byte</code><code>[]) object, session);</code>

<code>            </code><code>else</code> <code>if</code> <code>(object </code><code>instanceof</code> <code>Map) {</code>

<code>                </code><code>return</code> <code>createMessageForMap((Map) object, session);</code>

<code>            </code><code>else</code> <code>if</code> <code>(object </code><code>instanceof</code> <code>Serializable) {</code>

<code>                </code><code>return</code> <code>createMessageForSerializable(((Serializable) object), session);</code>

<code>            </code><code>else</code> <code>{</code>

<code>                </code><code>throw</code> <code>new</code> <code>MessageConversionException(</code><code>"Cannot convert object of type ["</code> <code>+</code>

<code>                        </code><code>ObjectUtils.nullSafeClassName(object) + </code><code>"] to JMS message. Supported message "</code> <code>+</code>

<code>                        </code><code>"payloads are: String, byte array, Map&lt;String,?&gt;, Serializable object."</code><code>);</code>

<code>        </code><code>public</code> <code>Object fromMessage(Message message) </code><code>throws</code> <code>JMSException, MessageConversionException {</code>

<code>            </code><code>if</code> <code>(message </code><code>instanceof</code> <code>TextMessage) {</code>

<code>                </code><code>return</code> <code>extractStringFromMessage((TextMessage) message);</code>

<code>            </code><code>else</code> <code>if</code> <code>(message </code><code>instanceof</code> <code>BytesMessage) {</code>

<code>                </code><code>return</code> <code>extractByteArrayFromMessage((BytesMessage) message);</code>

<code>            </code><code>else</code> <code>if</code> <code>(message </code><code>instanceof</code> <code>MapMessage) {</code>

<code>                </code><code>return</code> <code>extractMapFromMessage((MapMessage) message);</code>

<code>            </code><code>else</code> <code>if</code> <code>(message </code><code>instanceof</code> <code>ObjectMessage) {</code>

<code>                </code><code>return</code> <code>extractSerializableFromMessage((ObjectMessage) message);</code>

<code>                </code><code>return</code> <code>message;</code>

<code>        </code><code>protected</code> <code>TextMessage createMessageForString(String text, Session session) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>return</code> <code>session.createTextMessage(text);</code>

<code>        </code><code>protected</code> <code>BytesMessage createMessageForByteArray(</code><code>byte</code><code>[] bytes, Session session) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>BytesMessage message = session.createBytesMessage();</code>

<code>            </code><code>message.writeBytes(bytes);</code>

<code>            </code><code>return</code> <code>message;</code>

<code>        </code><code>protected</code> <code>MapMessage createMessageForMap(Map&lt;?, ?&gt; map, Session session) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>MapMessage message = session.createMapMessage();</code>

<code>            </code><code>for</code> <code>(Map.Entry entry : map.entrySet()) {</code>

<code>                </code><code>if</code> <code>(!(entry.getKey() </code><code>instanceof</code> <code>String)) {</code>

<code>                    </code><code>throw</code> <code>new</code> <code>MessageConversionException(</code><code>"Cannot convert non-String key of type ["</code> <code>+</code>

<code>                            </code><code>ObjectUtils.nullSafeClassName(entry.getKey()) + </code><code>"] to JMS MapMessage entry"</code><code>);</code>

<code>                </code><code>message.setObject((String) entry.getKey(), entry.getValue());</code>

<code>        </code><code>protected</code> <code>ObjectMessage createMessageForSerializable(Serializable object, Session session) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>return</code> <code>session.createObjectMessage(object);</code>

<code>        </code><code>protected</code> <code>String extractStringFromMessage(TextMessage message) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>return</code> <code>message.getText();</code>

<code>        </code><code>protected</code> <code>byte</code><code>[] extractByteArrayFromMessage(BytesMessage message) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>byte</code><code>[] bytes = </code><code>new</code> <code>byte</code><code>[(</code><code>int</code><code>) message.getBodyLength()];</code>

<code>            </code><code>message.readBytes(bytes);</code>

<code>            </code><code>return</code> <code>bytes;</code>

<code>        </code><code>protected</code> <code>Map extractMapFromMessage(MapMessage message) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>Map&lt;String, Object&gt; map = </code><code>new</code> <code>HashMap&lt;String, Object&gt;();</code>

<code>            </code><code>Enumeration en = message.getMapNames();</code>

<code>            </code><code>while</code> <code>(en.hasMoreElements()) {</code>

<code>                </code><code>String key = (String) en.nextElement();</code>

<code>                </code><code>map.put(key, message.getObject(key));</code>

<code>            </code><code>return</code> <code>map;</code>

<code>        </code><code>protected</code> <code>Serializable extractSerializableFromMessage(ObjectMessage message) </code><code>throws</code> <code>JMSException {</code>

<code>            </code><code>return</code> <code>message.getObject();</code>

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

本文轉自yunlielai51CTO部落格,原文連結:http://blog.51cto.com/4925054/1283326,如需轉載請自行聯系原作者