摘要
Spring Framework是一个流行的Java/JEE应用框架它构建于一个轻量级的反向控制(InversionofControlQoC)模式的容器的基础之上以其数据访问和事务管理能力而着称Spring的声明性事务划分适用于任何的POJO(pure old java object或plain ordinary Java object无格式普通Java对象)目标对象其声明性事务如同EJB容器托管事务(ContainerManaged TransactionCMT)一样完善后端事务管理器的选择包括简单的基于JDBC的事务和完善的JEE事务(借助于JTA策略)
本文详细讨论了Spring的事务管理功能重点介绍了如何以JTA作为后端事务策略使用Spring的针对POJO的声明性事务本文说明了Spring的事务服务可以与JEE服务器的事务协调程序(如BEA WebLogic Server的事务协调程序)进行无缝交互实际上已经成为EJB CMT的传统事务划分方式的替代方案
针对POJO的声明性事务
为了说明Spring的声明性事务划分方式让我们来看看Spring的PetClinic示例应用程序的中央服务外观(facade)的配置
<bean id=dataSource
class=orgspringframeworkjndiJndiObjectFactoryBean>
<property name=jndiName>
<value>java:comp/env/jdbc/petclinic</value>
</property>
</bean>
<bean id=transactionManager
class=orgspringframeworktransactionjtaJtaTransactionManager/>
<bean id=clinicTarget
class=orgspringframeworksamplespetclinicjdbcJdbcClinic>
<property name=dataSource><ref bean=dataSource/></property>
</bean>
<bean id=clinic
class=orgspringframeworktransactioninterceptorTransactionProxyFactoryBean>
<property name=transactionManager><ref bean=transactionManager/></property>
<property name=target><ref bean=clinicTarget/></property>
<property name=transactionAttributes>
<props>
<prop key=load*>PROPAGATION_REQUIREDreadOnly</prop>
<prop key=store*>PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
它遵从了Spring的标准XMLBean定义格式它定义了
一个DataSource引用指向一个JNDI位置这将从JEE服务器托管的JNDI环境中获取指定的DataSource
一个PlatformTransactionManage实现在本例中该实现指定Spring的JtaTransactionManager它委托给JEE服务器的事务协调程序
应用程序服务实现这是一个简单的POJO它封装了业务和数据访问逻辑它实现应用程序的Clinic服务接口
一个应用程序服务的事务代理该代理定义了目标服务的事务属性提供具体的方法命名模式并创建相应的事务对于实际的事务管理代理指向PlatformTransactionManager实现
注意Spring还通过通用属性(Commons Attribute)或者JSE 的注释(annotation)支持一种自动代理机制和对源级(sourcelevel)元数据的使用作为显示代理定义的替代方案这些替代方案不在本文的讨论范围之内其详细资料请参考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) {
thisdataSource = dataSource;
}
public Pet loadPet(int id) {
try {
Connection con = thisdataSourcegetConnection();
}
catch (SQLException ex) {
}
}
public void storePet(Pet pet) {
try {
Connection con = thisdataSourcegetConnection();
}
catch (SQLException ex) {
}
}
}
正如您所看到的代码简单明了使用了一个简单Java对象事务管理由事务代理处理我们随后再对其进行说明
注意PetClinic示例应用程序中实际的基于JDBC的Clinic实现利用了Spring的JDBC支持类以免只工作在简单的JDBC API级别上但是Spring的事务管理还将使用简单的基于JDBC的实现比如上面的实现
定义事务代理
除JdbcClinic实例之外配置还为其定义了一个事务代理如果需要可以显式地指定该事务代理所暴露的实际接口默认状态下目标对象实现的所有接口都将被暴露在本例中是应用程序的Clinic服务接口
从客户端的角度来看clinicbean只是应用程序的Clinic接口的实现客户端不必知道自己正在和事务代理打交道这就是接口的力量目标对象的直接引用可以很轻松地由实现了相同接口的代理取代在本例中是一个隐式地创建事务的代理
对于特定的方法或方法命名模式代理的具体事务行为由事务属性驱动如下面的例子所示
<prop key=load*>PROPAGATION_REQUIREDreadOnly</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 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)或者通过JTA支持嵌套事务
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务这是一个最优化提示在一些情况下一些事务策略能够起到显着的最优化效果例如在使用Object/Relational映射工具(如Hibernate或TopLink)时避免dirty checking(试图刷新)
在事务属性中还有定义timeout值的选项指定事务超时为几秒在JTA中这将被简单地传递到JEE服务器的事务协调程序并据此得到相应的解释
使用事务代理
在运行时客户端将获取到clinicbean的引用将其转换为Clinic接口同时在它上面调用诸如loadPet或storePet之类的操作这将隐式地使用在目标对象之前注册的事务拦截器检查Spring的事务代理新的事务将被创建然后调用将被委派给JdbcClinic目标方法
图说明了一个具有advisor链和终端目标的AOP代理的底层概念其中唯一的advisor就是将事务行为包装到目标方法的事务拦截器这是在Spring的声明性事务功能的帮助下产生的基于代理的AOP(面向方面编程)
educitycn/img_///jpg >图 具有advisor链和终端目标的AOP代理
例如PetClinic web应用程序中的web层组件能够执行ServletContext查询操作来获取对Spring WebApplicationContext的引用然后获得那里托管的Clinicbean
WebApplicationContext ctx =
WebApplicationContexUtilsgetWebApplicationContext(servletContext);
Clinic clinic = (Clinic) ctxgetBean(clinic);
Pet pet = new Pet();
petsetName(my new cat);
clinicstorePet(pet);在storePet()调用的开始Spring的事务代理将隐式地创建一个事务在storePet()调用返回时将提交或回滚事务默认情况下任何RuntimeException或Error的抛出均会导致回滚可以指定何时提交和何时回滚的实际规则Spring的事务属性支持一个称为回滚规则的概念
例如我们可以引入一个检查性的PetClinicException并告诉事务代理在抛出该异常时执行进行回滚
<prop key=load*>PROPAGATION_REQUIREDreadOnlyPetClinicException</prop>
<