Spring - Java/J2EE Application Framework 应用框架 第 14 章 JMS支持
第?14?章?JMS支持
14.1.?介紹
Spring提供一個用于簡化JMS API使用的抽象層框架,并且對用戶屏蔽JMS API中從1.0.2到1.1版本之間的不同。
JMS大體上被分為兩個功能塊,消息生產和消息消費。在J2EE環境,由消息驅動的bean提供了異步消費消息的能力 。而在獨立的應用中,則必須創建MessageListener或ConnectionConsumer來消費消息。 JmsTemplate的主要功能就是產生消息。Spring的未來版本將會提供,在一個獨立的環境中處理異步消息。
org.springframework.jms.core包提供使用JMS的核心功能。 就象為JDBC提供的JdbcTemplate一樣, 它提供了JMS模板類來處理資源的創建和釋放以簡化JMS的使用。 這個Spring的模板類的公共設計原則就是通過提供helper方法去執行公共的操作, 以及將實際的處理任務委派到用戶實現的回調接口上,從而以完成更復雜的操作。 JMS模板遵循這樣的設計原則。這些類提供眾多便利的方法來發送消息、異步地接收消息、 將JMS會話和消息產生者暴露給用戶。
org.springframework.jms.support包提供JMSException的轉換功能。 它將checked JMSException級別轉換到一個對應的unchecked異常級別, 任何checked的javax.jms.JMSException異常的子類都被包裝到unchecked的UncategorizedJmsException。org.springframework.jms.support.converter?包提供一個MessageConverter的抽象進行Java對象和JMS消息之間的轉換。?org.springframework.jms.support.destination提供多種管理JMS目的地的策略, 例如為存儲在JNDI中的目的地提供一個服務定位器。
最后,org.springframework.jms.connection包提供一個適合在獨立應用中使用的 ConnectionFactory的實現。它還為JMS提供了一個Spring的PlatformTransactionManager的實現。 這讓JMS作為一個事務資源和Spring的事務管理機制可以集成在一起使用。
14.2.?域的統一
JMS主要發布了兩個規范版本,1.0.2和1.1。JMS1.0.2定義了兩種消息域, 點對點(隊列)和發布/訂閱(主題)。 JMS1.0.2的API為每個消息領域提供了類似的類體系來處理這兩種不同的消息域。 結果,客戶端應用在使用JMS API時要了解是在使用哪種消息域。 JMS 1.1引進了統一域的概念來最小化這兩種域之間功能和客戶端API的差別。 舉個例子,如果你使用的是一個JMS 1.1的消息供應者, 你可以使用同一個Session事務性地在一個域接收一個消息后并且從另一個域中產生一個消息。
JMS 1.1的規范發布于2002年4月,并且在2003年11月成為J2EE 1.4的一個組成部分, 結果,現在大多數使用的應用服務器只支持JMS 1.0.2的規范.
14.3.?JmsTemplate
這里為JmsTemplate提供了兩個實現。JmsTemplate類使用JMS 1.1的API, 而子類JmsTemplate102使用了JMS 1.0.2的API。使用JmsTemplate的代碼只需要實現規范中定義的回調接口。 在JmsTemplate中通過調用代碼讓MessageCreator回調接口用所提供的會話(Session)創建消息。 然而,為了顧及更復雜的JMS API應用,回調接口SessionCallback將JMS會話提供給用戶, 并且暴露Session和MessageProducer。
JMS API暴露兩種發送方法,一種接受交付模式、優先級和存活時間作為服務質量(QOS)參數, 而另一種使用缺省值作為QOS參數(無需參數)方式。由于在JmsTemplate中有多種發送方法, QOS參數用bean屬性進行暴露設置,從而避免在一系列發送方法中重復。同樣地, 使用setReceiveTimeout屬性值來設置用于異步接收調用的超時值。
某些JMS供應者允許通過ConnectionFactory的配置來設置缺省的QOS值。 這樣在調用MessageProducer的發送方法send(Destination destination, Message message)?時效率更高,因為調用時直接會使用QOS缺省值,而不再用JMS規范中定義的值。 所以,為了提供對QOS值域的統一管理,JmsTemplate必須通過設置布爾值屬性isExplicitQosEnabled?為true,使它能夠使用自己的QOS值。
14.3.1.?ConnectionFactory
JmsTemplate請求一個對ConnectionFactory的引用。?ConnectionFactory是JMS規范的一部分,并被作為使用JMS的入口。 客戶端應用通常作為一個工廠配合JMS提供者去創建連接,并封裝一系列的配置參數, 其中一些是和供應商相關的,例如SSL的配置選項。
當在EJB內使用JMS時,供應商提供JMS接口的實現,以至于可以參與聲明式事務的管理, 提供連接池和會話池。為了使用這個實現,J2EE容器一般要求你在EJB或servlet部署描述符中將JMS連接工廠聲明為?resource-ref。為確保可以在EJB內使用JmsTemplate的這些特性, 客戶應用應當確保它能引用其中的ConnectionFactory實現。
Spring提供ConnectionFactory接口的一個實現,SingleConnectionFactory, 它將在所有的createConnection調用中返回一個相同的連接, 并忽略close的調用。這在測試和獨立的環境中相當有用, 因為同一個連接可以被用于多個JmsTemplate調用以跨越多個事務。 SingleConnectionFactory接受一個通常來自JNDI的標準ConnectionFactory的引用。
14.3.2.?事務管理
Spring為單個JMS ConnectionFactory提供一個JmsTransactionManager來管理事務。 它允許JMS應用可以利用第7章中描述的Spring的事務管理特性。JmsTransactionManager?從指定的ConnectionFactory將一個Connection/Session對綁定到線程。然而,在一個J2EE環境, ConnectionFactory將緩存連接和會話,所以被綁定到線程的實例依賴于緩存的行為。 在一個獨立的環境,使用Spring的SingleConnectionFactory將導致使用單獨的JMS連接, 而且每個連接都有自己的會話。JmsTemplate也能和JtaTransactionManager?以及XA-capable的JMS ConnectionFactory一起使用以完成分布式事務。
當使用JMS API從連接創建一個Session時,跨越管理性和非管理性事務的復用代碼可能會讓人困惑。這是因為JMS API只提供一個工廠方法來創建會話,并且它要求事務和確認模式的值。在受管理的環境下,由事務結構環境負責設置這些值,這樣在供應商包裝的JMS連接中可以忽略這些值。當在一個非管理性的環境中使用JmsTemplate時,你可以通過使用屬性SessionTransacted和SessionAcknowledgeMode來指定這些值。當在JmsTemplate中使用PlatformTransactionManager時,模板將一直被賦予一個事務性JMS會話。
14.3.3.?Destination管理
Destination,象ConnectionFactories一樣,是可以在JNDI中進行存儲和提取的JMS管理對象。當配置一個Spring應用上下文,可以使用JNDI工廠類JndiObjectFactoryBean在你的對象引用上執行依賴注入到JMS Destination。然而,如果在應用中有大量的Destination,或者JMS供應商提供了特有的高級Destination管理特性,這個策略常常顯得很笨重。高級Destination管理的例子如創建動態destination或支持destination的命名層次。JmsTemplate將destination名字到JMS destination對象的解析委派到一個DestinationResolver接口的實現。DynamicDestinationResolver是JmsTemplate?使用的默認實現,并且提供動態destination解析。同時JndiDestinationResolver作為JNDI包含的destination的服務定位器,并且可選擇地退回來使用DynamicDestinationResolver提供的行為。
相當常見的是在一個JMS應用中所使用的destination只有在運行時才知道,因此,當一個應用被部署時,它不能被創建。這經常是因為交互系統組件之間的共享應用邏輯是在運行時按照已知的命名規范創建destination。雖然動態destination的創建不是JMS規范的一部分,但是許多供應商已經提供了這個功能。用戶為所建的動態destination定義名字,這樣區別于來臨時destination,并且動態destination不會被注冊到JNDI中。創建動態destination所使用的API在不同的供應商之間差別很大,因為destination所關聯的屬性是供應商特有的。然而,有時由供應商作出的一個簡單的實現選擇是忽略JMS規范中的警告,并使用TopicSession的方法createTopic(String topicName)或者QueueSession的方法createQueue(String queueName)來創建一個擁有默認屬性的新destination。依賴于供應商的實現,DynamicDestinationResolver可能也能創建一個物理上的destination,而不是只是解析。
布爾屬性PubSubDomain被用來配置JmsTemplate使用什么樣的JMS域。這個屬性的默認值是false,使用點到點的域,也就是隊列。在1.0.2的實現中,這個屬性值用來決定JmsTemplate將消息發送到一個隊列還是主題。這個標志在1.1的實現中對發送操作沒有影響。然而,在這兩個實現中,這個屬性決定了通過DestinationResolver的實現來解析動態destination的行為。
你還可以通過屬性DefaultDestination配置一個帶有默認destination的JmsTemplate。默認的destination被使用時,它的發送和接收操作不需要指定一個特定的destination。
14.4.?使用JmsTemplate
要開始使用JmsTemplate前,你需要選擇JMS 1.0.2的實現,JmsTemplate102,還是JMS 1.1的實現,JmsTemplate。檢查一下你的JMS供應者支持那個版本。
14.4.1.?發送消息
JmsTemplate包含許多有用的方法來發送消息。這些發送方法可以使用javax.jms.Destination對象指定destination,也可以使用字符串在JNDI中查找destination。沒有destination參數的發送方法使用默認的destination。這里有個例子使用1.0.2的實現發送消息到一個隊列。
import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Queue; import javax.jms.Session;import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.JmsTemplate102; import org.springframework.jms.core.MessageCreator;public class JmsQueueSender {private JmsTemplate jt;private ConnectionFactory connFactory;private Queue queue;public void simpleSend() {jt = new JmsTemplate102(connFactory, false);jt.send(queue, new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage("hello queue world");}});}public void setConnectionFactory(ConnectionFactory cf) {connFactory = cf;}public void setQueue(Queue q) {queue = q;}}這個例子使用MessageCreator回調接口從所提供的會話對象中創建一個文本消息,并且通過一個ConnectionFactory的引用和指定消息域的布爾值來創建JmsTemplate。BeanFactory使用一個沒有參數的構造方法和setConnectionFactory/Queue方法來用構造實例。simpleSend方法在下面修改為發送消息到一個主題而不是隊列。
public void simpleSend() {jt = new JmsTemplate102(connFactory, true);jt.send(topic, new MessageCreator() {public Message createMessage(Session session) throws JMSException {return session.createTextMessage("hello topic world");}}); }當在應用上下文中配置JMS 1.0.2時,重要的是記得設定布爾屬性?PubSubDomain的值以確定你是要發送到隊列還是主題。
方法send(String destinationName, MessageCreator c)讓你利用destination的名字發送消息。如果這個名字在JNDI中注冊,你應當將模板中的DesinationResolver屬性設置為JndiDestinationResolver的一個實例。
如果你創建JmsTemplate并指定一個默認的destination,send(MessageCreator c)發送消息到這個destination。
14.4.2.?同步接收
雖然JMS一般都是應用在異步操作,但它也可能同步接收消息。重載的receive方法就提供這個功能。在同步接收時,調用線程被阻塞直到收到一個消息。這是一個危險的操作,因為調用線程可能會被無限期的阻塞。receiveTimeout屬性指定接收者在放棄等待一個消息前要等多久。
14.4.3.?使用消息轉換器
為了更容易的發送域模式對象,JmsTemplate有多種將一個Java對象作為消息數據內容的發送方法。在JmsTemplate中重載方法convertAndSend和receiveAndConvert,可以將轉換過程委派到MessageConverter接口的一個實例。這個接口定義了一個簡單的Java對象和JMS消息之間進行轉換的約定。它的默認實現SimpleMessageConverter支持在String和TextMessage,byte[]和BytesMesssage,java.util.Map和MapMessage之間進行轉換。通過使用轉換器,你的應用代碼可以專注于通過JMS發送或接收的業務對象,并不用為了怎樣將它描述為一個JMS消息而費心。
沙箱目前包含MapMessageConverter,它使用反射在JavaBean和MapMessage之間進行轉換。你還可以選擇使用XML組包的轉換器,如JAXB、Castor、XMLBeans或Xstream,來創建一個TextMessage來描述該對象。
消息屬性、消息頭和消息體的設置,一般不能被封裝在一個轉換器類中,為了調整它們,接口MessagePostProcessor可以使你在消息轉換后,發送前,訪問消息。下面的例子展示了如何在一個java.util.Map被轉換為消息之后修改一個消息的頭和屬性。
public void sendWithConversion() {Map m = new HashMap();m.put("Name", "Mark");m.put("Age", new Integer(35));jt.convertAndSend("testQueue", m, new MessagePostProcessor() {public Message postProcessMessage(Message message)throws JMSException {message.setIntProperty("AccountID", 1234);message.setJMSCorrelationID("123-00001");return message;}}); }這是一個由上面得到的消息
MapMessage={ Header={ ... standard headers ...CorrelationID={123-00001} } Properties={ AccountID={Integer:1234}} Fields={ Name={String:Mark} Age={Integer:35} } }14.4.4.?SessionCallback和ProducerCallback
雖然發送操作涵蓋了很多普通的使用場景,但是有些情況你需要在JMS Session或MessageProducer上執行多個操作。SessionCallback和ProduerCallback分別暴露了JMS Session和Session/MessageProducer對。JmsTemplate的execute()方法會執行這些接口上的回調方法。
from:?http://docs.huihoo.com/spring/zh-cn/jms.html
總結
以上是生活随笔為你收集整理的Spring - Java/J2EE Application Framework 应用框架 第 14 章 JMS支持的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring - Java/J2EE A
- 下一篇: Spring - Java/J2EE A