一、背景
在Spring事务:编程式事务管理这一篇中简单的介绍了Spring中的编程式事务管理,本篇介绍下Spring中声明式事务管理。
二、声明式事务管理
1、Spring中声明式事务管理概述
Spring中的声明式事务管理在底层是建立在AOP的基础上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完目标方法之后,根据执行情况再提交或回滚。
声明式事务最大的优点是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中加入事务管理代码,只需要在配置文件中做相关的事务规则声明,或者通过等价的基于标注的方式,便可以将事务应用到业务逻辑中。
声明式事务管理主要得益于Spring依赖注入容器和Spring AOP的支持。依赖注入容器为声明式事务管理提供了基础设施,是的bean对于Spring框架而言是可管理的;Spring AOP是声明式事务管理的直接观察者,这一点可以通过对应的配置文件得到证实。
与编程式事务管理相比,声明式唯一的不足是声明式事务管理的粒度只能作用在方法级别,无法像编程式事务那样可以作用到代码块级别。但是,这点不足之处,有很多变通的方法,比如,可以将需要事务管理的代码块单独定义成一个方法,等等。
从绝大多数资深的开发人员的经验上看,他们更倾向于使用声明式事务管理,这是因为声明式事务管理不仅简单,而且业务代码很纯粹,不会收到污染,方便后期的维护。
2、基于TransactionInterceptor的声明式使馆管理
Spring提供了TransactionInterceptor类来实施声明式事务管理功能。配置文件如下:
......
PROPAGATION_REQUIRED
......
首先,我们配置了一个TransactionInterceptor来定义相关的事务规则,它有两个主要的属性:一个是transactionManager,用来指定一个事务管理器,并将具体事务相关的操作委托给它;另一个是Properties类型的transactionAttributes属性,它主要用来定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性。指定事务属性的取值有较复杂的规则,这在Spring中算得上是一件让人头疼的事。具体的书写规则如下:
传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]
传播行为是唯一必须设置的属性,其他都可以忽略,Spring为我们提供了合理的默认值。传播行为的取值必须以“PROPAGATION_”开头,共七种取值,如下表所示:
PROPAGATION_MANDATORY |
PROPAGATION_NESTED |
PROPAGATION_NEVER |
PROPAGATION_NOT_SUPPORTED |
PROPAGATION_REQUIRED |
PROPAGATION_REQUIRES_NEW |
PROPAGATION_SUPPORTS |
隔离级别的取值必须以“ISOLATION_”开头,共五种取值,如下表所示:
ISOLATION_DEFAULT |
ISOLATION_READ_COMMITTED |
ISOLATION_READ_UNCOMMITTED |
ISOLATION_REPEATABLE_READ |
ISOLATION_SERIALIZABLE |
如果事务是只读的,那么我们可以指定只读属性,使用“readOnly”指定。否则我们不需要设置该属性。超时属性的取值必须以“TIMEOUT_”开头,后面跟一个int类型的值,表示超时时间,单位是秒。不影响提交的异常是指,即使事务中抛出了这些类型的异常,事务任然正常提交。必须在每一个异常的名字前面加上“+”。异常的名字可以是类名的一部分。比如,
“+RuntimeException”、“+tion”等等。导致回滚的异常是指,当事务中抛出这些类型的异常时,事务将回滚。必须在每一个异常的名字前面加上“-”。异常的名字可以是类名的全部或者部分,比如“-RuntimeException”、“-tion”等等。下面给出两个实例:
PROPAGATION_REQUIRED,
ISOLATION_READ_COMMITTED,
TIMEOUT_20,
+AbcException,
+DefException,
-HijException
以上表达式表示,针对所有方法名以Service结尾的方法,使用PROPAGATION_REQUIRED事务传播行为,事务的隔离级别是ISOLATION_READ_COMMITTED,超时时间为20秒,当事务抛出AbcException或者DefException类型的异常,则仍然提交,当抛出HijException类型的异常时必须回滚事务。这里没有指定"readOnly",表示事务不是只读的。
PROPAGATION_REQUIRED,
readOnly
以上表达式表示,针对所有方法名为test的方法,使用PROPAGATION_REQUIRED事务传播行为,并且该事务是只读的。除此之外,其他的属性均使用默认值。比如,隔离级别和超时时间使用底层事务性资源的默认值,并且当发生未检查异常,则回滚事务,发生已检查异常则仍提交事务。
配置好了TransactionInterceptor,还需要配置一个ProxyFactoryBean来组装target和advice。这是典型的Spring AOP的做法。通过ProxyFactoryBean生成的代理类就是织入了事务管理逻辑后的目标类。至此,声明式事务管理就算是实现了。我们没有对业务代码进行任何操作,所有设置均在配置文件中完成,这就是声明式事务的最大优点。
3、基于TransactionProxy...的声明式事务管理
前面的声明式事务虽然好,但是却存在一个非常恼人的问题:配置文件太多。必须针对每一个目标对象配置一个ProxyFactoryBean;另外,虽然可以通过父子Bean的方式来复用TransactionInterceptor的配置,但是实际的复用几率也不高;这样,加上目标对象本身,每一个业务类可能需要对应三个
为了缓解这个问题,Spring提供了TransactionProxyFactoryBean,用于将TransactionInterceptor 和ProxyFactoryBean的配置合二为一。如下面的代码所示:
......
PROPAGATION_REQUIRED
......
如此一来,配置文件与先前相比简化了很多,这种配置方式称为Spring经典的声明式事务管理。但是,显式为每一个业务类配置一个TransactionProxyFactoryBean的做法将使得代码显得过于刻板,为此可以使用自动创建代理的方式来将其简化,使用自动创建代理是纯 AOP 知识,请读者参考相关文档。
4、基于
前面两种声明式事务配置方式奠定了Spring声明式事务管理的基石。在此基础上,Spring 2.x 引入了
......
......