Blog信息 |
blog名称: 日志总数:1304 评论数量:2242 留言数量:5 访问次数:7573815 建立时间:2006年5月29日 |

| |
[Spring]使用Spring进行JMS消息传递 软件技术, 电脑与网络
lhwork 发表于 2006/7/5 14:04:05 |
我碰巧看到了一篇文章,是有关使用Spring框架来简化与IBM WebSphere MQ的交互的。这篇文章是对Spring中的JMS支持的相当不错的介绍,但是有一些重要的东西它却没有提到。
Spring作为J2EE框架的地位,与最近BEA宣布在WebLogic中对Spring提供正式支持这则消息结合起来,就会使一些开发人员认为,
文章中的代码可以不加修改地用于在WebLogic上运行的J2EE应用程序(很有可能,大部分人会选择使用WebLogic
startup类而不是基于文件的JDNI来映射MQ ConnectionFactory和队列到WebLogic
JNDI命名空间中,或通过支持Foreign JMS提供者来映射)。
那么,把文章中的代码用于在WebLogic上运行的J2EE应用程序又会产生什么结果呢?如预料中的一样,它确实运行了——消息被发送和交付,没有出现异常,所以乍一看一切都很正常。直到您将其用于CMT或BMT事务,比如说下面来自一个会话bean的代码:
代码:
/**
* @ejb.bean
* type="Stateless"
* name="SpringTest"
* view-type="local"
* transaction-type="Container"
*
* @ejb.transaction
* type="RequiresNew"
*/
public class SpringTestBean implements SessionBean {
...
/** @ejb.interface-method */
public void sendMessage() throws Exception {
JmsSender jmsSender =
(JmsSender)springContext.getBean("jmsSender");
jmsSender.sendMesage("test");
// rollback CMT transaction
sessionContext.setRollbackOnly();
}
...
}
本来事务回滚之后,消息应该不在MQ中,但是实际情况是消息在MQ中。其原因非常简单——WebLogic需要使用特定的包装器来包装MQ
ConnectionFactory对象,以确保在XA事务上下文中获取正确的资源。仅仅将对象放入WebLogic
JNDI是不够的。开发人员应该通过EJB部署描述符中的resource-ref元素声明ConnectionFactory:
代码:
<resource-ref>
<res-ref-name>myQcf</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
...
<resource-description>
<res-ref-name>myQcf</res-ref-name>
<jndi-name>mq.qcf</jndi-name>
</resource-description>
然后,在Spring上下文定义中,QueueConnectionFactory应该引用通过resource-ref映射的名字:
<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/myQcf"/>
</bean>
然后,在Spring上下文定义中,QueueConnectionFactory应该引用通过resource-ref映射的名字:
代码:
<resource-ref>
<res-ref-name>myQcf</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
...
<resource-description>
<res-ref-name>myQcf</res-ref-name>
<jndi-name>mq.qcf</jndi-name>
</resource-description>
然后,在Spring上下文定义中,QueueConnectionFactory应该引用通过resource-ref映射的名字:
<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/myQcf"/>
</bean>
注意,该工厂是在java:comp/env命名空间中,而不是在JNDI全局作用域中进行查找。这将确保WebLogic所使用的将要参与全局事务的ConnectionFactory对象经过正确包装,然后上面的例子就会如预料那样运行了。
虽然上面的例子正常运行了,但是还是有一些问题。因为现在所有使用Spring的JMS对象的操作都应该由某个正确定义了resource-
ref的EJB发起。这意味着,例如,开发人员要非常小心,不要把JMSSender作为依赖注入到某个可以不作为EJB调用序列(例如,调度程序之类)
的一部分而执行的类或需要访问多个ConnectionFactory的类中。另一种方法是扩展Spring的
JndiObjectFactoryBean类,支持创建所要求的包装器。这种方法的问题是,(据我所知)该包装器API还没有说明文档。
所以,最后结论是,最好不要假设Spring会“魔法”,然后就期待一切发生,还是要经常测试,确保它真的 管用。
评论
* 确实,没有魔法:即使在一个基于Spring的应用程序中,资源引用也需要得到正确定义,就像普通的J2EE应用程序与这些资源交互时一样。
resource-ref元素也可以在web.xml中声明,所以同样的功能也完全适用于普通的web应用程序(WAR部署单元),而不是只适用于
EJB。这是Spring应用程序直接与应用服务器的服务连接的最典型的场景:使用Spring驱动的事务,用
JtaTransactionManager作为后端,JndiObjectFactoryBean定义指向resource-ref
JNDI位置(一个JDBC DataSource或一个JMS ConnectionFactory)。
发表人:juergen.hoeller,2005年10月20日,01:59 PM
* 是的,也可以在WAR中使用resource-ref,而且还要更好一些,因为可以以全局级别指定resource-ref,而对于EJB,如果我没有弄错的话,是针对每个bean。
如果定义了多个ConnectionFactory,一定要小心。因为常见的实践是在不同EJB的同一个“逻辑”名下定义它们,而且它们可以调用同一个共享的组件。
发表人:maximdim,2005年10月20日,02:33 PM
http://dev2dev.bea.com/blog/maximdim/archive/2005/10/jms_messaging_w.html
作者简介
Dmitri Maximovich是一位独立顾问,专长是软件设计、开发和技术培训。他具有超过12年的从业经验,而且很早就开始涉及J2EE。他的工作包括为金融业和医药业设计和开发任务关键的应用程序。 |
|
|