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