代码: |
/** * @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会“魔法”,然后就期待一切发生,还是要经常测试,确保它真的 管用。