使用Spring JMS轻松实现异步消息传递
图1.贷款处理程序的序列图 (单击截图来查看完整视图)
下面的表3显示了在例程中我所使用的不同技术和开源框架,并按应用逻辑层排列。
表3. 在JMS应用程序中使用的框架
<!--CreditRequestSendQueue-->
<mbeancode="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=CreditRequestSendQueue">
<dependsoptional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean>
<!--CreditRequestReceiveQueue-->
<mbeancode="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=CreditRequestReceiveQueue">
<dependsoptional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean> 现在,让我们看看如何使用一个名为Hermes的JMS工具来浏览消息队列。Hermes是一个Java Swing应用程序,它可以创建、管理和监视JMS提供商(例如JBossMQ,WebSphereMQ,ActiveMQ和Arjuna服务器)里的JMS目标。从它的网站上下载Hermes,解压缩.zip文件到本地目录(例如,c:/dev/tools/hermes)。一旦安装完成,双击文件hermes.bat(位于bin文件夹下)启动程序。要在Hermes里配置JBossMQ服务器,请参考Hermes网站上的这个演示。它有着出色的step-by-step可视化指示来配置JBoss MQ。当配置一个新的JNDI初始上下文时,请输入下面的信息。
providerURL = jnp://localhost:1099initialContextFactory = org.jnp.interfaces.NamingContextFactoryurlPkgPrefixes = org.jnp.interfaces:org.jboss.namingsecurityCredentials = adminsecurityPrincipal = admin当您创建新的目标时,请输入queue/CreditRequestSendQueue和queue/CreditRequestReceiveQueue。图2显示了JMS控制台的主窗口,其中有为JMS例程创建的新的消息队列。

图 2. Hermes中所有目标的截图.(单击截图来查看完整视图)
下面的图3显示了在从消息发送者类发送消息到CreditRequestSendQueue后,Hermes JMS控制台及消息队列的截图。您可以看见有5个消息在队列中,控制台显示了消息详情,例如消息ID,消息目标,时间戳和实际的消息内容。

图 3. Hermes中所有队列的截图.(单击截图来查看完整视图)
在例程中使用的队列名称和其他JMS和JNDI参数见表 4。
表4. Spring JMS配置参数
<beanid="jndiTemplate"class="org.springframework.jndi.JndiTemplate">
<propertyname="environment">
<props>
<propkey="java.naming.factory.initial">
org.jnp.interfaces.NamingContextFactory
</prop>
<propkey="java.naming.provider.url">
localhost
</prop>
<propkey="java.naming.factory.url.pkgs">
org.jnp.interfaces:org.jboss.naming
</prop>
</props>
</property>
</bean> 接着,我们配置队列连接工厂。清单3显示了队列连接工厂的配置。清单3. JMS队列连接工厂配置
?
<beanid="jmsQueueConnectionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean">
<propertyname="jndiTemplate">
<refbean="jndiTemplate"/>
</property>
<propertyname="jndiName">
<value>UIL2ConnectionFactory</value>
</property>
</bean> 我们定义2个JMS目标来发送和接收消息。详情见清单4和5。清单4. 发送队列配置
<beanid="sendDestination"
class="org.springframework.jndi.JndiObjectFactoryBean">
<propertyname="jndiTemplate">
<refbean="jndiTemplate"/>
</property>
<propertyname="jndiName">
<value>queue/CreditRequestSendQueue</value>
</property>
</bean> 清单5. 接收队列配置?
<beanid="receiveDestination"
class="org.springframework.jndi.JndiObjectFactoryBean">
<propertyname="jndiTemplate">
<refbean="jndiTemplate"/>
</property>
<propertyname="jndiName">
<value>queue/CreditReqeustReceiveQueue</value>
</property>
</bean> 然后我们再来配置JmsTemplate组件。在例程中我们使用JmsTemplate102。同时使用defaultDestination属性来指定JMS目标。清单6. JMS模板配置
?
<beanid="jmsTemplate"
class="org.springframework.jms.core.JmsTemplate102">
<propertyname="connectionFactory">
<refbean="jmsQueueConnectionFactory"/>
</property>
<propertyname="defaultDestination">
<refbean="destination"/>
</property>
<propertyname="receiveTimeout">
<value>30000</value>
</property>
</bean> 最后我们配置发送者和接收者组件。清单7和8分别是Sender 和 Receiver对象的配置。清单7. JMS Sender配置
?
<beanid="jmsSender"class="springexample.client.JMSSender">
<propertyname="jmsTemplate">
<refbean="jmsTemplate"/>
</property>
</bean> 清单8. JMS Receiver配置
?
<beanid="jmsReceiver"class="springexample.client.JMSReceiver">
<propertyname="jmsTemplate">
<refbean="jmsTemplate"/>
</property>
</bean> 我写了一个测试类,命名为LoanApplicationControllerTest,用来测试LoanProc程序。我们可以使用这个类来设定贷款参数以及调用信用请求服务类。
让我们看一下不使用Spring JMS API而使用传统JMS开发途径的消息发送者实例。清单9显示了MessageSenderJMS类里的sendMessage方法,其中包含了使用JMS API处理消息的所有必需步骤。
清单9. 传统JMS实例
?
publicvoidsendMessage()...{
queueName="queue/CreditRequestSendQueue";
System.out.println("Queuenameis"+queueName);
/**//*
*CreateJNDIInitialContext
*/
try...{
Hashtableenv=newHashtable();
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=newInitialContext(env);
}catch(NamingExceptione)...{
System.out.println("CouldnotcreateJNDIAPI"+
"context:"+e.toString());
}
/**//*
*GetqueueconnectionfactoryandqueueobjectsfromJNDIcontext.
*/
try...{
queueConnectionFactory=(QueueConnectionFactory)
jndiContext.lookup("UIL2ConnectionFactory");
queue=(Queue)jndiContext.lookup(queueName);
}catch(NamingExceptione)...{
System.out.println("JNDIAPIlookupfailed:"+
e.toString());
}
/**//*
*Createconnection,session,senderobjects.
*Sendthemessage.
*CleanupJMSconnection.
*/
try...{
queueConnection=
queueConnectionFactory.createQueueConnection();
queueSession=queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
queueSender=queueSession.createSender(queue);
message=queueSession.createTextMessage();
message.setText("ThisisasampleJMSmessage.");
System.out.println("Sendingmessage:"+message.getText());
queueSender.send(message);
}catch(JMSExceptione)...{
System.out.println("Exceptionoccurred:"+e.toString());
}finally...{
if(queueConnection!=null)...{
try...{
queueConnection.close();
}catch(JMSExceptione)...{}
}
}
} 现在,我们来看看使用了Spring的消息发送者实例。清单10显示了MessageSenderSpringJMS类中send方法的代码。清单10. 使用Spring API的JMS实例
?
publicvoidsend()...{
try...{
ClassPathXmlApplicationContextappContext=newClassPathXmlApplicationContext(newString[]...{
"spring-jms.xml"});
System.out.println("Classpathloaded");
JMSSenderjmsSender=(JMSSender)appContext.getBean("jmsSender");
jmsSender.sendMesage();
System.out.println("MessagesentusingSpringJMS.");
}catch(Exceptione)...{
e.printStackTrace();
}
} 如您所见,通过使用配置文件,所有与管理JMS资源有关的步骤都将交由Spring容器处理。我们只需引用一个JMSSender对象,然后调用对象里的sendMessage方法。结束语在本文中,我们看到Spring框架是如何使用JMS API简化异步消息传递。Spring去掉了所有使用JMS处理消息所必需的样本代码(例如得到一个队列连接工厂,从Java代码里创建队列和会话对象, 在运行时使用配置文件对它们进行组配)。我们可以动态的交换JMS资源对象,而不必修改任何Java代码,这要感谢Inversion of Control (IOC) 原则的力量。
既然异步消息传递是SOA框架的整体构成部分,Spring很适合纳入到SOA工具集。此外,JMS管理工具(如Hermes)使得创建、管理和监督JMS资源变得容易,特别是对于系统管理员来说。
参考资料本文配套的示例代码 Spring JMS documentation"1-2-3 Messaging with Spring JMS"JBoss MQ wiki