Spring声明式事务管理
概述
Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。因为事务管理本身就是一个典型的横切逻辑,正是 AOP 的用武之地。Spring 开发团队也意识到了这一点,为声明式事务提供了简单而强大的支持。
声明式事务管理曾经是 EJB 引以为傲的一个亮点,如今 Spring 让 POJO 在事务管理方面也拥有了和 EJB 一样的待遇,让开发人员在 EJB 容器之外也用上了强大的声明式事务管理功能,这主要得益于 Spring 依赖注入容器和 Spring AOP 的支持。依赖注入容器为声明式事务管理提供了基础设施,使得 Bean 对于 Spring 框架而言是可管理的;而 Spring AOP 则是声明式事务管理的直接实现者。
和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
Spring的声明式事务管理是基于AOP的,而AOP是通过动态代理实现的。动态代理的一个重要特征是:它是针对接口的,所以如果dao或者service要通过动态代理让Spring接管事务,就必须在dao或者service抽象一个接口。常见的形式比如:1
2
3
4
5接口类:IUserService
实现类:UserService
或者
接口类:UserService
实现类:UserServiceImpl
当然如果没有这样的接口,那么Spring会使用CGLib来解决问题,但这不是Spring推荐的方式。
Spring VS EJB
EJB CMT和Spring声明式事务管理之间是有相似的,但也有很多不同。其基本方法是相似的:都可以指定事务到单独的方法、如果需要可以在事务上下文调用setRollbackOnly()方法。不同之处在于:
- Spring声明式事务管理可以在任何环境下使用,只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事务机制一起工作。而EJB CMT要绑定在JTA上。
- Spring可以使声明式事务管理应用到普通Java对象
- Spring允许你通过AOP定制事务行为。例如,如果需要,你可以在事务回滚中插入定制的行为。你也可以增加任意的通知,就象事务通知一样。使用 EJB CMT,除了使用setRollbackOnly(),你没有办法能够影响容器的事务管理。
- Spring提供声明式回滚规则:EJB没有对应的特性。回滚可以声明式控制,不仅仅是编程式的。
Spring的声明式事务管理
基于tx命名空间的声明式事务管理
1 | <!-- 配置Spring的事务处理 --> |
基于@Transactional的声明式事务管理
除了基于tx命名空间的事务配置方式,Spring还引入了基于 Annotation 的方式,具体主要涉及 @Transactional
标注。@Transactional
可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。1
2
3
4@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) {
return bankDao.transfer(fromId, toId, amount);
}
Spring 使用 BeanPostProcessor 来处理 Bean 中的标注,因此我们需要在配置文件中作如下声明来激活该后处理 Bean:1
<tx:annotation-driven transaction-manager="transactionManager"/>
虽然 @Transactional
注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 小组建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional
注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional
注解,这将被忽略,也不会抛出任何异常。
基于 @Transactional
的事务声明方式各有优缺点。基于 @Transactional
的方式必须在每一个需要使用事务的方法或者类上用 @Transactional
标注,尽管可能大多数事务的规则是一致的,但是对 @Transactional
而言,也无法重用,必须逐个指定。另一方面,基于 @Transactional
的方式使用起来非常简单明了,没有学习成本。