技术分享 在Spring中实现事务挂起(图)
2008-01-05 10:08:49 来源:WEB开发网核心提示:摘要SPRing Framework是一个流行的java/J2EE应用框架,它构建于一个轻量级的反向控制(Inversion-of-Control,技术分享 在Spring中实现事务挂起(图),QoC)模式的容器的基础之上,以其数据访问和事务治理能力而著称,我们可以引入一个检查性的PetClinicException,
摘要
SPRing Framework是一个流行的java/J2EE应用框架,它构建于一个轻量级的反向控制(Inversion-of-Control,QoC)模式的容器的基础之上,以其数据访问和事务治理能力而著称。Spring的声明性事务划分适用于任何的POJO(pure old java object或plain ordinary Java object,无格式普通Java对象)目标对象,其声明性事务如同EJB容器托管事务(Container-Managed Transaction,CMT)一样完善。后端事务治理器的选择包括简单的基于JDBC的事务和完善的J2EE事务(借助于JTA策略)。
本文具体讨论了Spring的事务治理功能。重点介绍了如何以JTA作为后端事务策略,使用Spring的针对POJO的声明性事务。本文说明了Spring的事务服务可以与J2EE服务器的事务协调程序(如BEA WebLogic Server的事务协调程序)进行无缝交互,实际上已经成为EJB CMT的传统事务划分方式的替代方案。
针对POJO的声明性事务
为了说明Spring的声明性事务划分方式,让我们来看看Spring的PetClinic示例应用程序的中心服务外观(facade)的配置:
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/petclinic</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/>
<bean id="clinicTarget"
class="org.springframework.samples.petclinic.jdbc.JdbcClinic">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<bean id="clinic"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="clinicTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
它遵从了Spring的标准xmlBean定义格式。它定义了:
一个DataSource引用,指向一个JNDI位置--这将从J2EE服务器托管的JNDI环境中获取指定的DataSource。
一个PlatformTransactionManage实现--在本例中,该实现指定Spring的JtaTransactionManager,它委托给J2EE服务器的事务协调程序。
应用程序服务实现--这是一个简单的POJO,它封装了业务和数据访问逻辑。它实现应用程序的Clinic服务接口。
一个应用程序服务的事务代理--该代理定义了目标服务的事务属性,提供具体的方法命名模式,并创建相应的事务。对于实际的事务治理,代理指向PlatformTransactionManager实现。
注重:Spring还通过通用属性(Commons Attribute)或者J2SE 5.0的注释(annotation),支持一种自动代理机制和对源级(source-level)元数据的使用,作为显示代理定义的替代方案。这些替代方案不在本文的讨论范围之内;其具体资料请参考Spring说明文档。
使用的服务接口和服务实现是特定于应用程序的,无需了解Spring(具体说是Spring的事务治理)就可以实现。纯Java对象可以用作目标对象,而任何一个纯Java接口都可以用作服务接口。下面是一个Clinic接口的例子:
public interface Clinic {
Pet loadPet(int id);
void storePet(Pet pet);
...
}
下面显示了该接口的一个简单实现,假定它使用JDBC来执行必要的数据访问。它通过一个bean属性的setter方法接收JDBC DataSource,这直接对应上面配置中的dataSource属性定义。
public class JdbcClinic implements Clinic {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Pet loadPet(int id) {
try {
Connection con = this.dataSource.getConnection();
...
}
catch (SQLException ex) {
...
}
}
public void storePet(Pet pet) {
try {
Connection con = this.dataSource.getConnection();
...
}
catch (SQLException ex) {
...
}
}
...
}
正如您所看到的,代码简单明了。使用了一个简单Java对象。事务治理由事务代理处理,我们随后再对其进行说明。
注重,PetClinic示例应用程序中实际的基于JDBC的Clinic实现利用了Spring的JDBC支持类,以免只工作在简单的JDBC API级别上。但是,Spring的事务治理还将使用简单的基于JDBC的实现,比如上面的实现。
定义事务代理
除JdbcClinic实例之外,配置还为其定义了一个事务代理。假如需要,可以显式地指定该事务代理所暴露的实际接口。默认状态下,目标对象实现的所有接口都将被暴露--在本例中是应用程序的Clinic服务接口。
从客户端的角度来看,“clinic”bean只是应用程序的Clinic接口的实现。客户端不必知道自己正在和事务代理打交道。这就是接口的力量:目标对象的直接引用可以很轻松地由实现了相同接口的代理取代--在本例中是一个隐式地创建事务的代理。
对于特定的方法或方法命名模式,代理的具体事务行为由事务属性驱动,如下面的例子所示:
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop><prop key="store*">PROPAGATION_REQUIRED</prop>
key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:
PROPAGATION_REQUIRED--支持当前事务,假如当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,假如当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,假如当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,假如当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,假如当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,假如当前存在事务,则抛出异常。
PROPAGATION_NESTED--假如当前存在事务,则在嵌套事务内执行。假如当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJB CMT:常量名相同,因此,对EJB开发人员来说,应该马上就感到熟悉。第七个(PROPAGATION_NESTED)是Spring所提供的一个非凡变量。它要求事务治理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager),或者通过JTA支持嵌套事务。
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。
在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。
使用事务代理
在运行时,客户端将获取到“clinic”bean的引用,将其转换为Clinic接口,同时在它上面调用诸如loadPet或storePet之类的操作。这将隐式地使用在目标对象之前注册的“事务拦截器”检查Spring的事务代理;新的事务将被创建,然后调用将被委派给JdbcClinic目标方法。
图1说明了一个具有“advisor链”和终端目标的AOP代理的底层概念。其中,唯一的advisor就是将事务行为包装到目标方法的事务拦截器。这是在Spring的声明性事务功能的帮助下产生的基于代理的AOP(面向方面编程)。
图1. 具有“advisor链”和终端目标的AOP代理
例如,PetClinic web应用程序中的web层组件能够执行ServletContext查询操作来获取对Spring WebapplicationContext的引用,然后获得那里托管的“Clinic”bean:
WebApplicationContext ctx =
WebApplicationContexUtils.getWebApplicationContext(servletContext);
Clinic clinic = (Clinic) ctx.getBean("clinic);
Pet pet = new Pet();
pet.setName("my new cat");
clinic.storePet(pet); 在storePet()调用的开始,Spring的事务代理将隐式地创建一个事务。在storePet()调用返回时,将提交或回滚事务。默认情况下,任何RuntimeException或Error的抛出均会导致回滚。可以指定何时提交和何时回滚的实际规则:Spring的事务属性支持一个称为“回滚规则”的概念。
例如,我们可以引入一个检查性的PetClinicException,并告诉事务代理,在抛出该异常时执行进行回滚。
<prop key="load*">PROPAGATION_REQUIRED,readOnly,-PetClinicException</prop>
<
[]
更多精彩
赞助商链接