概述
JmsTemplate提供了3組*3,共計9個發送用的方法。
發送的方法有3組:
- 基本的發送
- 轉換並發送
- 轉換、后處理再發送
必需的資源
必需的資源有:
- javax.jms.ConnectionFactory
ConnectionFactory是客戶端編程的開始,由它依次獲取Connection、Session、Message、MessageProducer或MessageConsumer,從而做好了發送或接收的准備。 - javax.jms.Destination
Destination是發送的目的地,或者接收的源
實際上,我們提供了上述的資源之后,我們就是做了這樣的指令:讓JmsTemplate連接到
Destination,從我們提供的
ConnectionFactory中獲取連接資源。
P.S.如果你不了客戶端編程模型,建議參考
02. JMS客戶端編程模型。
1.ConnectionFactory資源
我們如何提供ConnectionFactory給JmsTemplate?憑借
JmsTemplete提供的構造器或setter方法:
- public JmsTemplate(ConnectionFactory connectionFactory)
- public void setConnectionFactory(ConnectionFactory connectionFactory)
# 繼承自org.springframework.jms.support.JmsAccessor
2.Destination資源
我們如何提供
Destination給JmsTemplate?
我們有兩個機會做這件事,第一次是在初始化的時候提供一個默認的Destination,第二次是在發送的時候提供一個明確的Destination。相關的方法:
- public void setDefaultDestination(Destination destination)
設置默認的Destination - public void send(Destination destination, MessageCreator messageCreator)
將消息發送到指定的Destination
MessageCreator接口用來提供創建消息的回調方法,后面再講。
JmsTemplate還提供了另一種獲取Destination的方式:基於Destination解析器、Destination類型、Destination名字的獲取。你可以使用下面的代碼來指定解析器、Destination類型:
1 // jt is instance of JmsTemplate 2 jt.setDestinationResolver(new DynamicDestinationResolver()); // set Destination解析器 3 jt.setPubSubDomain(false); // set Destinantion類型
Destination解析器要實現org.springframework.jms.support.destination.DestinationResolver接口。解析器有默認的值,就是DynamicDestinationResolver,除非你要使用其他的解析器,否則不必調用setDestinationResolver。所以,第2行代碼是多余的。
Destination類型有2個:false-Queue類型,true-Topic類型;默認為false。所以,第3行代碼是多余的。
設置了解析器、類型,或者直接使用默認的值,之后,就可以設置destination的name。JmsTemplate提供了兩個方法:
- public void setDefaultDestinationName(String destinationName)
設置defaultDestination。這個方法和setDefaultDestination(Destination destination)做同樣的事情,只是這個方法依賴於解析器和類型。 - public void send(String destinationName, MessageCreator messageCreator)
將消息發送到指定的Destination。這個方法和send(Destination destination, MessageCreator messageCreator)做同樣的事情,只是這個方法依賴於解析器和類型。
P.S.如何根據destinationName創建Destination實例?
javax.jms.Session提供了兩個方法,分別創建兩種類型的Destination:
- Queue createQueue(java.lang.String queueName)
根據name創建Queue類型的Destination - Topic createTopic(java.lang.String topicName)
根據name創建Topic類型的Destination
我們看下DynamicDestinationResolver的源碼:
1 public class DynamicDestinationResolver implements DestinationResolver { 2 @Override 3 public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) 4 throws JMSException { 5 Assert.notNull(session, "Session must not be null"); 6 Assert.notNull(destinationName, "Destination name must not be null"); 7 if (pubSubDomain) { 8 return resolveTopic(session, destinationName); 9 } 10 else { 11 return resolveQueue(session, destinationName); 12 } 13 } 14 protected Topic resolveTopic(Session session, String topicName) throws JMSException { 15 return session.createTopic(topicName); 16 } 17 protected Queue resolveQueue(Session session, String queueName) throws JMSException { 18 return session.createQueue(queueName); 19 } 20 }
根據這份源碼,可以幫助理解pubSubDomain的機制,以及將JMS的api和Spring的Destination解析器這兩個知識點連接起來。
1.基本的發送方法
在前文我們已經接觸了兩個,它們都是在發送的同時指定Destination。現把它們和第3種一起介紹:
- public void send(Destination destination, MessageCreator messageCreator)
將消息發送到指定的Destination - public void send(String destinationName, MessageCreator messageCreator)
將消息發送到指定的Destination。這個方法和send(Destination destination, MessageCreator messageCreator)做同樣的事情,只是這個方法依賴於解析器和類型。 - public void send(MessageCreator messageCreator)
將消息發送到defaultDestination。
這個方法要求提前設置defaultDestination,你可以調用setDefaultDestination(Destination destination)或者setDefaultDestinationName(String destinationName)來滿足這個前提。
在3個基本的發送方法中,都使用MessageCreator來創建消息。
使用MessageCreator創建消息
jms中的Message,是以接口javax.jms.Message為首的接口家族,這個家族的圖譜是這樣的:
javax.jms.Message
|---- BytesMessage
|---- MapMessage
|---- ObjectMessage
|---- StreamMessage
|---- TextMessage
JMS將Message細分為5種類型,並在javax.jms.Session接口中分別定義了創建上述Message的多個方法,通常以create*Message為名,返回對應的Message類型。
在JMS的api中,只有javax.jms.Session能創建消息。
所以在Spring中,如果我們要創建
Message,就要有Session。但是我們只有現成的ConnectionFactory,我們不應該走一遍從ConnectionFactory到Session的路,否則我們也不需要JmsTemplate幫我們發送了,因為剩下的工作也沒多少了——關鍵是我們並沒有從JMS的API中解脫出來。
所以有了
MessageCreator的接口,它定義了一個回調的方法:
- Message createMessage(Session session)
只要我們把MessageCreator的實例傳給JmsTemplate,它就會在合適的時候調用這個方法,並發送返回的消息。
下面給一個例子:
1 jt.send(DESTINATION_NAME, new MessageCreator() { 2 3 public Message createMessage(Session session) throws JMSException { 4 String text = "A test for MessageCreator."; 5 Message message = session.createTextMessage(text); 6 return message; 7 } 8 });
2.轉換並發送的方法
我們需要將數據裝進JMS的Message,然后再發送。JMS的Message有5種具體的類型,不同的類型適合裝載不同的數據。如果你不想做這段工作,而是希望能直接把數據丟給誰,然后由它來封裝成Message——你只想准備數據,然后發送。那么接下來要介紹的方法,正適合你。
Spring為轉換定義了一個接口:org.springframework.jms.support.converter.MessageConverter,這個接口定義了下面的兩個方法:
- Message toMessage(Object object, Session session)
發送時用到 - Object fromMessage(Message message)
接收時用到
一般情況下,我們既不需要為
MessageConverter提供實現,也不需要面向
MessageConverter進行編程,所以我們實在沒有必要關注上面的兩個方法,掃一眼,有個大概的印象就夠了。
說回JmsTemplate,它定義了下面的方法來設置Converter:
- public void setMessageConverter(MessageConverter messageConverter)
而且在初始化的時候,會自動賦值一個SimpleMessageConverter類型的實例,所以我們甚至也不需要關心
setMessageConverter方法了。
說了這不多,總結一下,如果要用到轉換,我們需要多做什么工作?
答案是不需要!
下面是具有轉換功能的發送的方法,與基本的發送的方法進行對比:
轉換發送 | 基本發送 |
|
|
這兩個系列的方法相似度很高,只是在創建消息的問題上有不同的處理:轉換發送隱藏了消息的創建,基本發送需要實現
MessageCreator接口來創建消息。
接下來是一個demo,我們將上面的demo也拿下來,做一個對比:
轉換發送 |
1 String message = "a message for test convertAndSend."; 2 jt.convertAndSend(DESTINATION_NAME, message); |
基本發送 |
1 jt.send(DESTINATION_NAME, new MessageCreator() { 2 3 public Message createMessage(Session session) throws JMSException { 4 String text = "A test for MessageCreator."; 5 Message message = session.createTextMessage(text); 6 return message; 7 } 8 }); |
3.轉換、后處理再發送的方法
javax.jms.Message定義了很多的方法用來為消息添加頭部信息或屬性。但是如果我們要用轉換並發送的方法,我們就接觸不到Message類型的消息了,自然也無法為其添加任何信息。JmsTemplate提供了另一套發送的方法,允許我們使用自動轉換,還允許我們能接觸到轉換后的消息,以便我們能做些什么。之后我們會返回處理后的Message,交給JmsTemplate發送。
Spring定義了org.springframework.jms.core.MessagePostProcessor接口來做后處理的事,它定義了一個唯一的方法:
- Message postProcessMessage(Message message)
對消息進行處理,並返回處理后的消息
讓我們來看看這些方法,並與前文介紹的方法對比:
轉換、后處理、發送 | 轉換、發送 |
|
|
再來看看兩個demo的對比:
轉換、后處理、發送 |
1 String message = "a message for test convertProcessAndSend."; 2 jt.convertAndSend(DESTINATION_NAME, message, 3 new MessagePostProcessor() { 4 public Message postProcessMessage(Message message) 5 throws JMSException { 6 message.setIntProperty("order", 1); 7 return message; 8 } 9 }); |
轉換、發送 |
1 String message = "a message for test convertAndSend."; 2 jt.convertAndSend(DESTINATION_NAME, message); |