?Spring pd 的第 4 期也是最后一期中Q我介l?Spring JMSQJava 消息服务Q框架的Ҏ。JMS PG 定义?Java 应用E序通过面向消息的中间gQMOMQ创建和交换消息的标准途径?/p>
像在这个系列前面的文章中一P我将使用一个简单的CZ来演C?Spring JMS 的特性。您随我一道开发一个点对点的(P2PQ基于消息的pȝQ?Spring JMS 框架通过 JMS 接口?IBM ?WebSphere MQ 集成。完成练习后Q将可以通过q个pȝ发送和接收单的文本消息?/p>
在开始之前,?下蝲文章的源代码。请参阅 参考资?/font> 讉K Spring 框架?IBM WebSphere MQ 5.3。还需?Apache Ant 来运行示例应用程序?/p>
Spring ?JMS 抽象框架化了 JMS API 的用,q与 JMS 提供者(比如 IBM ?WebSphere MQ 5.3Q^滑地集成?i>org.springframework.jms.core 包提供了?Spring 中?JMS 的核心功能。它的模板类处理资源的创建和释放Q简化了 JMS 的用?/p>
像其他大多数 Spring 模板cMPJMS 模板cL供了执行公共操作?helper Ҏ。在需要更复杂应用的情况下Q类把处理Q务的核心委托l用户实现的回调接口。JMS cL供了方便的方法,用来发送消息、同步地使用消息以及向用户公开 JMS 会话和消息的制作者?/p>
以下 JMS 包和 org.springframework.jms.core 一h成了 Spring JMS 的功能:
JMSException
的功能。{换代码把到?JMSException
层次l构转换成未到异常的镜像层ơ结构?
MessageConverter
抽象Q以?Java 对象?JMS 消息之间q行转换?
ConnectionFactory
实现。connection q包含针?JMS ?Spring PlatformTransactionManager
实现。它允许?JMS 作ؓ事务性资源集成到 Spring 的事务管理机制中?
![]() ![]() |
![]()
|
像前面提到的,CZ应用E序会用 Spring ?JMS 框架通过 JMS 接口?IBM ?WebSphere MQ 集成。通过在应用程序和 Web 服务之间传递消息,WebSphere MQ 提供了可靠的、有恢复能力的应用程序集成。它使用队列和事务性工具帮助保持消息跨|络的完整性。WebSphere MQ 降低了信息丢q风险和调和通信 IT pȝ的需要?/p>
WebSphere MQ 在它所支持的所有^C提供了一致的应用E序~程接口Q这有助于让集成的程序可UL。除了标准接口外QWebSphere MQ q完整实CJMS 接口Q包括对发布-订阅消息传递的支持。WebSphere MQ Explorer 工具可以q程地管理和配置整个 MQ |络。管理和配置工具Z开放源码的 Eclipse 框架Q而且是可扩展的?/p>
![]() ![]() |
![]()
|
Spring 框架提供?JmsTemplate
的两个实现?code>JmsTemplate cM?JMS 1.1 APIQ子c?JmsTemplate102
则?JMS 1.0.2 API。我的示例应用程序用的?JmsTemplate102
?/p>
JMS 模板被用来发送和接收 JMS 消息。Spring 采用回调机制?JMS 信息传递进行协调?code>MessageCreator 回调接口?JmsTemplate
中的调用代码提供?Session
创徏消息。ؓ了支?JMS API 更复杂的应用Q回?SessionCallback
向用h供了 JMS 会话Q?callback ProducerCallback
则公开?Session
?MessageProducer
l合?
清单 1 昄了示例应用程序用的 JMS 模板的配|。清单摘?spring-mqseries-jms.xml 文gQ请参阅 下蝲Q?
<!-- JMS Queue Template --> <bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate102"> <property name="connectionFactory"> <ref bean="jmsQueueConnectionFactory"/> </property> <property name="destinationResolver"> <ref bean="jmsDestinationResolver"/> </property> <property name="pubSubDomain"> <value>false</value> </property> <property name="receiveTimeout"> <value>20000</value> </property> </bean> |
jmsQueueTemplate
bean ?JMS q接工厂?JMS 目标解析器绑定在一P用于解析 JMS 客户机通过 JNDI 提供的目标队列名?code>connectionFactory 属性指定了如何获得?JMS 提供者的q接。在本例中,清单 2 昄了如何从 JNDI 索连接工厂?/p>
清单 2. 通过 JNDI 配置 JMS q接工厂
<!-- JMS Queue Connection Factory --> <bean id="internalJmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate"> <ref bean="jndiTemplate"/> </property> <property name="jndiName"> <value>MQ_JMS_MANAGER</value> </property> </bean> |
可以看到Q?code>JndiObjectFactoryBean 被绑定到 internalJmsQueueConnectionFactory
?code>JndiObjectFactoryBean ?JndiTemplate
属性进?JNDI 查询。Spring 用 JndiTemplate
中指定的环境属性和初始上下文在 JNDI 中查询连接工厂。清?3 昄?JndiTemplate
配置 bean 的配|?/p>
清单 3. JNDI 查询?JNDI 模板配置
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"> <property name="environment"> <props> <prop key="java.naming.factory.initial"> com.sun.jndi.fscontext.RefFSContextFactory </prop> <prop key="java.naming.provider.url"> file:/C:/JNDI-Directory </prop> </props> </property> </bean> |
以上配置q行 JNDI 查询时用 com.sun.jndi.fscontext.RefFSContextFactory
指定初始上下文工厂,用基于文件的 file:/C:/JNDI-Directory 作ؓ提供?URL。根据示例应用程序的意图QJNDI 讉K会采用基于文件的 FSContext
版本Q请参阅 参考资?/font>Q的配置?MQ 队列l定?JNDI?/p>
有了定义好的 JMS 模板Q下一步就是把它绑定到CZ应用E序中,然后可以用它发送和接收消息了?/p>
![]() ![]() |
![]()
|
JMS 模板可以l定到应用程序中Q以发送和接收 JMS 消息。在清单 4 中可以看出我如何?清单 1 中的 JMS 模板l定到示例应用程序中?/p>
清单 4. ?JmsTemplate l定到应用程序中
<bean id="jmsSender" class="springexample.client.JMSSender"> <property name="jmsTemplate102"> <ref bean="jmsQueueTemplate"/> </property> </bean> <bean id="jmsReceiver" class="springexample.client.JMSReceiver"> <property name="jmsTemplate102"> <ref bean="jmsQueueTemplate"/> </property> </bean> |
可以看到Q我?jmsQueueTemplate
l定到用来发送和接收消息?JmsSender
应用E序 bean ?JmsReceiver
bean。清?5 昄了与 JMSSender
cL关的代码?/p>
清单 5. ?JmsTemplate 发?JMS 消息?JMSSender
public class JMSSender { private JmsTemplate102 jmsTemplate102; public JmsTemplate102 getJmsTemplate102() { return jmsTemplate102; } public void setJmsTemplate102(JmsTemplate102 jmsTemplate102) { this.jmsTemplate102 = jmsTemplate102; } public void sendMesage(){ jmsTemplate102.send("JMS_RequestResponseQueue", new MessageCreator() { public Message createMessage(Session session) throws JMSException { return session.createTextMessage("This is a sample message"); } }); } |
JMSSender
cȝ jmsTemplate102.send()
Ҏ发?JMS 消息?code>send() Ҏ的第一个参数是 JNDI 队列名,队列名指定了消息应当发送到哪里。(很快׃看到如何?WebSphere MQ 的队列名l定?JNDI。)send()
Ҏ的第二个参数?MessageCreator
cR?code>JmsTemplate 中的调用代码提供?Session
c,q个cL供了一个创?JMS 消息的回调接口?/p>
下一步是?JMS ?Session
cdZ个简单的文本消息。在代码执行Ӟ消息会传递给 WebSphere MQ 服务器的队列。清?6 昄了?JmsTemplate
?JMS 消息?JMSReceiver
应用E序 bean 的代码?/p>
清单 6. ?JmsTemplate ?JMS 消息?JMSReceiver
public class JMSReceiver { private JmsTemplate102 jmsTemplate102; public JmsTemplate102 getJmsTemplate102() { return jmsTemplate102; } public void setJmsTemplate102(JmsTemplate102 jmsTemplate102) { this.jmsTemplate102 = jmsTemplate102; } public void processMessage(){ Message msg = jmsTemplate102.receive("JMS_RequestResponseQueue"); try{ TextMessage textMessage = (TextMessage) msg; if( msg!=null){ System.out.println(" Message Received -->" + textMessage.getText()); } }catch(Exception e){ e.printStackTrace(); } } } |
JMSReceiver
cȝ jmsTemplate102.receive()
Ҏ同步地接?JMS 消息?code>receive() Ҏ指定 JNDI 队列名,q从中检索消息?code>JMSTemplate cȝ processMessage()
Ҏ由接?JMS 客户用?code>JSMTemplate bean 的属?receiveTimeoutQ列?JMSTemplate
配置中)指定接收客户机同步地从队列中接收消息时要{候的旉?/p>
现在应用E序的代码已完成Q下一步就是配|?WebSphere MQ 队列q把它们l定?JNDI 对象?/p>
![]() ![]() |
![]()
|
在运行应用程序之前,需要设|?WebSphere MQ 的队列管理器和队列,q把它们l定?JNDI。如果喜Ƣ的话,可以按照q部分的CZ做:只需 下蝲 讄 WebSphere MQ 队列的批文g和应用程序的源代码和部v描述W即可。把 zip 文g解压到驱动器 C:?/p>
讄队列
q行 C:\SpringSeriesPart4JMS\batch 文g夹中?mqsetup.bat 文g。这个批文g要求?path 环境变量中设|好 MQ 安装?bin 文g夹(例如 C:\mqseries\binQ。运行了Ҏ件之后,应当看到消息 ?code>All valid MQSC commands were processed”。要打开 MQ Explorer q检查已l创建的队列理器和队列Q请选择 Start -> Programs -> IBM MQSeries -> MQSeriesExplorer。图 1 昄出示例应用程?QueueManager
MQJMS.QManager
已经创徏q正在运行?/p>
?1. WebSphere MQ ?QueueManager 配置
请在应用E序屏幕左侧面板上点?MQJMS.QManager
下的 Queues 文g夏V应当看到已l创Z一个队?RequestResponseQueue
Q如?2 所C?/p>
?2. WebSphere MQ 的请?响应队列配置
q就完成了队列的讄?/p>
讄 JMS ?JNDI 理
在示例应用程序中QJNDI 的访问利用了可以?JNDI 主页得到的基于文件的 FSContext
版本Q请参阅 参考资?/font>Q?i>FSContext.jar 文g也包含在 WebSphere MQ ?JMS 支持当中。请d文g?\MQSeriesInstallable\MQSeries\Java\lib ?\MQSeriesInstallable\MQSeries\Java\bin 到系l的 PATH 环境变量中。而且Q请?\MQSeriesInstallable\MQSeries\Java\lib 文g夹中的所?jar 文gd到系l的 CLASSPATH 环境变量中。还可以q行 C:\SpringSeriesPart4JMS\batch 文g夹中?classpath.cmd 文gQ它会设|必要的 path ?CLASSPATH 变量。要做到q点Q只需要修?classpath.cmd 文g中的 MQ_JAVA_INSTALL_PATH
Q把它指?WebSphere MQ JMS 的安装目录?/p>
接下来,修改 \MQSeriesInstallableDirectory\Java\bin 中的 JMSAdmin.config 配置文gQMQSeries JMS 理E序用它指明应用E序要用的上下文工厂和 JNDI 实现的地址。请取消以下行的注释Q?/p>
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory |
q注释掉其余两个 INITIAL_CONTEXT_FACTORY
变量。还要取消以下行的注释:
PROVIDER_URL=file:/C:/JNDI-Directory |
q注释掉其余两个 PROVIDER_URL
变量?/p>
可以?C:\SpringSeriesPart4JMS\batch 文g夹中发现参考的CZ配置文g?/p>
Z保存 JNDI 对象Q请在驱动器 C: 上创建名?JNDI-Directory 的目录。切换到 \MQSeriesInstallableDirectory\Java\bin 目录q运?JMSAdmin ҎӞ应当看到 InitCtx 变量?/p>
逐个输入以下内容Q?/p>
def qcf(MQ_JMS_MANAGER) qmgr(MQJMS.QManager) |
现在已经?WebSphere MQ 队列l定?JNDI 对象Q作为应用程序客户可以通过 JNDI 查询对象。现在剩下的是看代码的实际作用了!
![]() ![]() |
![]()
|
要运行示例,请从 spring sourceforge download 下蝲 Spring 框架和它的所有依赖文件ƈ解压Q例如解压到 c:\。会创徏文g?C:\spring-framework-1.2-rc2Q或最新版本)?/p>
要运?Spring 应用E序Q请把本文的源代码解压到L文g夹,例如 c:\。会创徏文g?SpringSeriesPart4JMS。就像前面提到过的,q需要安?Apache Ant 和它?Spring 依赖 jar 文g。请?Spring ?—??spring.jarQ在 C:\spring-framework-1.2-rc2\dist 中)?commons-logging.jarQ在 C:\spring-framework-1.2-rc2\lib\jakarta-commons 中)拯?SpringSeriesPart4JMS\lib 文g夏V还要把所有的 jar 库从 \MQSeriesInstallableDirectory\Java\lib 目录拯?SpringSeriesPart4JMS\lib 文g夏V其中包?MQseries ?JMS 的相兛_。现在就拥有了构建的依赖集?/p>
接下来,打开命o提示W,切换?SpringProject4 目录Qƈ在命令提C符下输入以下命令:
> ant -f build-jmssender.xml. |
q会构徏q运?SendMQSpringJMS
c,它会调用 JMSSender
c,发送消息到 WebSphere MQ RequestResponse
队列?code>SendMQSpringJMS q会通过它的 ClassPathXmlApplicationContext
装入 spring 配置文g。一?bean 全部装蝲Q就可以通过 Spring ?ApplicationContext ?getBean()
Ҏ讉K JMSSenderQ请参阅清单 7Q?/p>
清单 7. 装入CZ应用E序?Spring 配置
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "spring-mqseries-jms.xml" }); JMSSender jmsSender = (JMSSender) appContext.getBean("jmsSender"); |
消息传递到队列上之后,误?JMS 接收方客h以检索消息。请打开命o提示W,切换到目?SpringProject4Qƈ输入Q?/p>
> ant -f build-jmsreceiver.xml |
q会构徏q运?ReceiveMQSpringJMS
c,该类会调?JMSReceiver
c,以从 WebSphere MQ ?RequestResponse
队列接收文本消息。在控制C会打印出以下消息Q?/p>
Message Received --> This is a sample message. |
?Spring pd的最后这文章中Q您学习?Spring JMS 框架的基。我首先介绍了示例应用程序的核心lg —?Spring JMS 框架?IBM ?WebSphere MQ 5.3Q然后介l了如何?Spring JMS 模板?WebSphere MQ 队列发送消息和从中接收消息。虽然这个示例非常简单,但是可以把这里介l的步骤应用到更复杂的应用程序?/p>
我希望介l?Spring 框架核心模块的这一pdҎ有所帮助。请参阅 参考资?/font> 学习更多有关 Spring 框架?Spring JMS 的内宏V?/p>
![]() ![]() |
![]()
|
描述 | 名字 | 大小 | 下蝲Ҏ |
---|---|---|---|
Example code, Spring files, and build scripts | wa-spring4-SpringSeriesPart4JMS.zip | 17 KB | FTP |
![]() |
||||
![]() |
关于下蝲Ҏ的信?/font> |
![]() |
![]() |
Get Adobe] Reader] |
Spring框架则简化了使用JEElg(包括JMS)的Q务。它提供的模板机刉藏了典型的JMS实现的细节,q样开发h员可以集中精力放在处理消息的实际工作中,而不用担心如何去创徏Q访问或清除JMS资源?/p>
本文对Spring JMS API作一个概qͼq过一个运行在JBoss MQ服务器上的web例程来介l如何用Spring JMS API来异步处理(发送和接收Q消息。我通过传统JMS实现和Spring JMS实现两者间的比较,来展CZ用Spring JMS处理消息是如何的单和灉|?/p>
在现实中Q大多数webh都是同步处理的。例如,当用戯d一个网站,首先输入用户名和密码Q然后服务器验证d合法性。如果验证成功,E序允许该用户q入|站。这里,dh在从客户端接收以后被x处理了。信用卡验证是另一个同步处理的例子Q只有服务器证实输入的信用卡h有效的,同时客户在帐户上有够的存款Q客h被允许l操作。但是让我们思考一下在序处理pȝ上的支付l算步骤。一旦系l证实该用户信用卡的信息是准的Qƈ且在帐户上有_的资金,׃必等到所有的支付l节落实、{账完成。支付结可以异步方式进行,q样客户可以l箋q行核查操作?/p>
需要比典型同步h耗费更长旉的请求,可以使用异步处理。另一个异步处理的例子是,在本地贷Ƒ֤理程序中Q提交至自动扉KpȝQAUSQ的信用h处理q程。当借方提交h甌后,抉|公司会向AUS发送请求,以获取信用历史记录。由于这个请求要求得到全面而又详细的信用报告,包括借方C和过ȝ帐户Q最q的付款和其他胦务资料,服务器需要耗费较长的时_几小时或着有时甚至是几天)来对q些h作出响应。客LE序Q应用)要与服务器连接ƈ耗费如此长的旉来等待结果,q是毫无意义的。因此通信应该是异步发生的Q也是Q一旦请求被提交Q它p攄在队列中Q同时客L与服务器断开q接。然后AUS服务从指定的队列中选出hq行处理Qƈ处理得到的消息攄在另一个消息队列里。最后,客户端程序从q个队列中选出处理l果Q紧接着处理q个信用历史数据?/p>
如果您用过JMS代码Q您会发现它与JDBC或JCA很像。它所包含的样本代码创建或JMS资源对象回溯Q得每一ơ您需要写一个新cL发送和接收消息Ӟ都具有更好的代码密集性和重复性。以下序列显CZ传统JMS实现所包括的步骤:
Spring框架提供了一个模板机制来隐藏Java APIs的细节。JEE开发h员可以用JDBCTemplate和JNDITemplatecL分别讉K后台数据库和JEE资源Q数据源Q连接池Q。JMS也不例外。Spring提供JMSTemplatec,因此开发h员不用ؓ一个JMS实现ȝ写样本代码。接下来是在开发JMS应用E序时Spring所h一些的优势?/p>
?. Spring JMSc?/p>
cd | ?/th> | 功能 |
---|---|---|
JmsException | org.springframework.jms | 只要发生一个JMS异常QSpring框架׃抛出异常Q这个类是这些所抛出的异常的基(抽象Q类?/td> |
JmsTemplate, JmsTemplate102 | org.springframework.jms.core | q些是辅助类Q用于简化JMS的用,处理JMS资源Q如q接工厂Q目标和发送?接收者对象)的创建和释放。JmsTemplate102是JmsTemplate的子c,使用JMS1.0.2规范 |
MessageCreator | org.springframework.jms.core | q是JmsTemplatecM用的回叫接口Q它为指定的会话创徏JMS消息?/td> |
MessageConverter | org.springframework.jms.support.converter | q个接口充当一个抽象,用来在Java对象与JMS消息之间q行转换?/td> |
DestinationResolver | org.springframework.jms.support.destination | q是JmsTemplate用来解析目标名的接口。该接口的默认实现是DynamicDestinationResolver和JndiDestinationResolve |
在接下来的部分,我将详细解释?所列的一部分c(例如JmsTemplateQDestinationResolver和MessageConverterQ?/p>
JmsTemplate提供了几U辅助方法,用来执行一些基本操作。要开始用JmsTemplate前,您需要知道JMS供应商支持哪个JMS规范Q?a target="_blank">JBoss AS 4.0.2?a target="_blank">WebLogic 8.1服务器支持JMS 1.0.2规范。WebLogic Server 9.0包括了对JMS 1.1规范?a target="_blank">支持。JMS 1.1l一了点对点QPTPQ和发布/订阅QPub/SubQ域的编E接口。这U改变的l果是Q开发h员可以创Z个事务会话,然后在这同一个JMS会话里,可以从一个Queue(PTP)中接收消息,同时发送另一个消息到一个Topic(Pub/Sub)。JMS 1.1向后兼容JMS 1.0Q应此根据JMS 1.0~写的代码仍可以适用于JMS 1.1?/p>
JmsTemplate提供多种发送和接收消息的方法。表2列出了这些方法的一部分?/p>
?. JMS templateҎ
Ҏ名称 | 功能 |
---|---|
send | 发送消息至默认或指定的目标。JmsTemplate包含sendҎQ它通过javax.jms.Destination或JNDI查询来指定目标?/td> |
receive | 从默认或指定的目标接收消息,但只会在指定的时间后传递消息。我们可以通过receiveTimeout属性指定超时时间?/td> |
convertAndSend | q个Ҏ委托MessageConverter接口实例处理转换q程Q然后发送消息至指定的目标?/td> |
receiveAndConvert | 从默认或指定的目标接收消息。ƈ消息{换ؓJava对象?/td> |
目标可以通过JNDI上下文保存和获取。当配置SpringE序上下文(application contextQ时Q我们可以用JndiObjectFactoryBeancd得对JMS的引用。DestinationResolver接口是用来把目标名称解析成JMS目标Q当应用E序存在大量目标Ӟq是非常有用的。DynamicDestinationResolver(DestinationResolver的默认实?是用来解析动态目标的?/p>
MessageConverter接口定义了将Java对象转换为JMS消息的约定。通过q个转换器,应用E序代码可以集中于处理事务对象,而不用ؓ对象如何表示为JMS消息q样的内部细节所困饶。SimpleMessageConverter(和SimpleMessageConverter102)是MessageConverter的默认实现。可使用它们分别String转换为JMS TextMessageQ字节数l?byte[])转换为JMS BytesMessageQMap转换为JMS MapMessageQ和Serializable对象转换为JMS ObjectMessage。您也可以编写自定义的MessageConverter实例Q通过XMLl定框架Q例?a target="_blank">JAXBQ?CastorQ?a target="_blank">Commons DigesterQ?a target="_blank">XMLBeans?a target="_blank">XStreamQ,来实现XML文档到TextMessage对象的{换?/p>
我将用一个贷Ƅ请处理系l(命名为LoanProcQ示例来演示如何在JMS应用E序中用Spring。作ƄL一部分QLoanProc通过发送贷ƾ详情(hIDQ借方名字Q借方的SSNQ贷ƾ期限和h数额Q,从AUSpȝ获得信用历史详情。ؓ了简便v见,我们Z两个基本参数来表CZ用历史详情:信用分数Q又名FICO得分Q和h数额。让我们假设处理信用查请求是按以下业务规则进行的Q?/p>
信用h处理使用案例包括以下几个步骤Q?/p>
在这个例E中Q两个消息队列都配置在同一个JBoss MQ server上。用案例用?的序列图QSequenceDiagramQ表C?/p>
下面的表3昄了在例程中我所使用的不同技术和开?/a>框架Qƈ按应用逻辑层排列?/p>
?. 在JMS应用E序中用的框架
逻辑?/th> | 技?框架 |
---|---|
MVC | Spring MVC |
Service | Spring Framework (version 2.1) |
JMS API | Spring JMS |
JMS Provider | JBoss MQ (version 4.0.2) |
JMS Console | Hermes |
IDE | Eclipse 3.1 |
Z异步处理消息Q首先我们需要消息队列发送和接收消息。我们可以用Jboss里的配置XML文g创徏一个新的消息队列,然后使用JMS控制台浏览队列的详细情况。清?昄了配|JMS的XML配置代码片断Q这个应该加入到jbossmq-destinations-service.xml文gQ位?JBOSS_HOME%\server\all\deploy-hasingleton\jm文g夹下。)
清单1.JBoss MQ Server上JMS队列的配|?/p>
<!-- Credit Request Send Queue --> <mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=CreditRequestSendQueue"> <depends optional-attribute-name="DestinationManager"> jboss.mq:service=DestinationManager </depends> </mbean> <!-- Credit Request Receive Queue --> <mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=CreditRequestReceiveQueue"> <depends optional-attribute-name="DestinationManager"> jboss.mq:service=DestinationManager </depends> </mbean>
现在Q让我们看看如何使用一个名为Hermes的JMS工具来浏览消息队列?a target="_blank">Hermes是一个Java Swing应用E序Q它可以创徏、管理和监视JMS提供商(例如JBossMQQ?a target="_blank">WebSphereMQQ?a target="_blank">ActiveMQ?a target="_blank">Arjuna服务器)里的JMS目标。从它的|站上下载HermesQ解压羃.zip文g到本地目录(例如Qc:\dev\tools\hermesQ。一旦安装完成,双击文ghermes.batQ位于bin文g夹下Q启动程序?/p>
要在Hermes里配|JBossMQ服务器,请参考Hermes|站上的q个演示。它有着的step-by-step可视化指C来配置JBoss MQ。当配置一个新的JNDI初始上下文时Q请输入下面的信息?/p>
当您创徏新的目标Ӟ误入queue/CreditRequestSendQueue和queue/CreditRequestReceiveQueue。图2昄了JMS控制台的ȝ口,其中有ؓJMS例程创徏的新的消息队列?/p>
?2. Hermes中所有目标的截图.Q单L图来查看完整视图Q?/p>
下面的图3昄了在从消息发送者类发送消息到CreditRequestSendQueue后,Hermes JMS控制台及消息队列的截图。您可以看见?个消息在队列中,控制台显CZ消息详情Q例如消息IDQ消息目标,旉戛_实际的消息内宏V?/p>
?3. Hermes中所有队列的截图.Q单L图来查看完整视图Q?/p>
在例E中使用的队列名U和其他JMS和JNDI参数见表 4?/p>
?. Spring JMS配置参数
参数名称 | 参数?/th> |
---|---|
Initial Context Factory | org.jnp.interfaces.NamingContextFactory |
Provider URL | localhost:8080 |
Initial Context Factory URL Packages | org.jnp.interfaces:org.jboss.naming |
Queue Connection Factory | UIL2ConnectionFactory |
Queue Name | queue/CreditRequestSendQueue, queue/CreditRequestReceiveQueue |
既然我们已经有了q行例程所需要的JMS目标Q现在该了解?a target="_blank">XML Spring配置文gQ名为spring-jms.xmlQ来l配JMSlg的具体细节了。这些组件是ҎInversion of Controller (IOC)设计模式里的讄方式注入原则Qsetter injection principleQ,用JMS对象实例cȝ配的。让我们详细查看q些lgQƈ为每一个JMSlg演示一DXML配置代码?/p>
JNDI上下文是取得JMS资源的v始位|,因此首先我们要配|JNDI模板。清?昄了名为jndiTemplate的Spring beanQ其中列有JNDI初始上下文所必需的常用参数?/p>
清单2. JNDI上下文模?/p>
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"> <property name="environment"> <props> <prop key="java.naming.factory.initial"> org.jnp.interfaces.NamingContextFactory </prop> <prop key="java.naming.provider.url"> localhost </prop> <prop key="java.naming.factory.url.pkgs"> org.jnp.interfaces:org.jboss.naming </prop> </props> </property> </bean>
接着Q我们配|队列连接工厂。清?昄了队列连接工厂的配置?/p>
清单3. JMS队列q接工厂配置
<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate"> <ref bean="jndiTemplate"/> </property> <property name="jndiName"> <value>UIL2ConnectionFactory</value> </property> </bean>
我们定义2个JMS目标来发送和接收消息。详情见清单4??/p>
清单4. 发送队列配|?/p>
<bean id="sendDestination" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate"> <ref bean="jndiTemplate"/> </property> <property name="jndiName"> <value>queue/CreditRequestSendQueue</value> </property> </bean>
清单5. 接收队列配置
<bean id="receiveDestination" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate"> <ref bean="jndiTemplate"/> </property> <property name="jndiName"> <value>queue/CreditReqeustReceiveQueue</value> </property> </bean>
然后我们再来配置JmsTemplatelg。在例程中我们用JmsTemplate102。同时用defaultDestination属性来指定JMS目标?/p>
清单6. JMS模板配置
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate102"> <property name="connectionFactory"> <ref bean="jmsQueueConnectionFactory"/> </property> <property name="defaultDestination"> <ref bean="destination"/> </property> <property name="receiveTimeout"> <value>30000</value> </property> </bean>
最后我们配|发送者和接收者组件。清??分别是Sender ?Receiver对象的配|?/p>
清单7. JMS Sender配置
<bean id="jmsSender" class="springexample.client.JMSSender"> <property name="jmsTemplate"> <ref bean="jmsTemplate"/> </property> </bean>
清单8. JMS Receiver配置
<bean id="jmsReceiver" class="springexample.client.JMSReceiver"> <property name="jmsTemplate"> <ref bean="jmsTemplate"/> </property> </bean>
我写了一个测试类Q命名ؓLoanApplicationControllerTestQ用来测试LoanProcE序。我们可以用这个类来设定贷Ƒ֏C及调用信用请求服务类?/p>
让我们看一下不使用Spring JMS API而用传lJMS开发途径的消息发送者实例。清?昄了MessageSenderJMSc里的sendMessageҎQ其中包含了使用JMS API处理消息的所有必需步骤?/p>
清单9. 传统JMS实例
public void sendMessage() { queueName = "queue/CreditRequestSendQueue"; System.out.println("Queue name is " + queueName); /* * Create JNDI Initial Context */ try { Hashtable env = new Hashtable(); env.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); env.put("java.naming.provider.url","localhost"); env.put("java.naming.factory.url.pkgs", "org.jnp.interfaces:org.jboss.naming"); jndiContext = new InitialContext(env); } catch (NamingException e) { System.out.println("Could not create JNDI API " + "context: " + e.toString()); } /* * Get queue connection factory and queue objects from JNDI context. */ try { queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("UIL2ConnectionFactory"); queue = (Queue) jndiContext.lookup(queueName); } catch (NamingException e) { System.out.println("JNDI API lookup failed: " + e.toString()); } /* * Create connection, session, sender objects. * Send the message. * Cleanup JMS connection. */ try { queueConnection = queueConnectionFactory.createQueueConnection(); queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); queueSender = queueSession.createSender(queue); message = queueSession.createTextMessage(); message.setText("This is a sample JMS message."); System.out.println("Sending message: " + message.getText()); queueSender.send(message); } catch (JMSException e) { System.out.println("Exception occurred: " + e.toString()); } finally { if (queueConnection != null) { try { queueConnection.close(); } catch (JMSException e) {} } } }
现在Q我们来看看使用了Spring的消息发送者实例。清?0昄了MessageSenderSpringJMScMsendҎ的代码?/p>
清单10. 使用Spring API的JMS实例
public void send() { try { ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "spring-jms.xml"}); System.out.println("Classpath loaded"); JMSSender jmsSender = (JMSSender)appContext.getBean("jmsSender"); jmsSender.sendMesage(); System.out.println("Message sent using Spring JMS."); } catch(Exception e) { e.printStackTrace(); } }
如您所见,通过使用配置文gQ所有与理JMS资源有关的步骤都交由Spring容器处理。我们只需引用一个JMSSender对象Q然后调用对象里的sendMessageҎ?/p>
在本文中Q我们看到Spring框架是如何用JMS API化异步消息传递。SpringL了所有用JMS处理消息所必需的样本代码(例如得到一个队列连接工厂,从Java代码里创建队列和会话对象Q在q行时用配|文件对它们q行l配Q。我们可以动态的交换JMS资源对象Q而不必修改Q何Java代码Q这要感谢Inversion of Control (IOC) 原则的力量?/p>
既然异步消息传递是SOA框架的整体构成部分,Spring很适合U_到SOA工具集。此外,JMS理工具Q如HermesQ得创建、管理和监督JMS资源变得ҎQ特别是对于pȝ理员来说?/p>
![]() |