摘要
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>
<
闽公网安备 35060202000074号