简介
spring支持编程式事务管理和声明式事务管理两种方式:
- 编程式事务是在代码中直接加入处理事务的逻辑,可能需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法。编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
- 声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
二者区别:编程式事务侵入性比较强,但处理粒度更细.
声明式事务
声明式事务是通过AOP来实现的,在Spring中,通过TransactionProxyFactoryBean生成代理对象,在代理对象中通过TransactionInterceptor来完成对代理方法的拦截。
事务拦截器的使用
配置TransactionProxyFactoryBean的XML文件
|
|
下面看一TransactionProxyFactoryBean的代码:
|
|
下面我们看一下transactionInterceptor是如何成为Advisor的,由于AbstractSingletonProxyFactoryBean类实现了InitializingBean接口,因此在Bean初始化过程完成依赖注入的时候调用了afterPropertiesSet方法,具体代码:
|
|
经过一系列的步骤,Spring的事务拦截器TransactionIntercepter(AOP的Advice)配置到ProxyFactory生成的AOP代理对象中。
事务拦截器的配置
TransactionInterceptor的父类TransactionAspectSupport中配置transactionAttributeSource属性
事务拦截器的拦截
在AOP中我们知道,在拦截器中会有一个invoke()方法,该方法是代理对象的回调方法,下面我们看一下TransactionInterceptor中的invoke方法
|
|
Spring的事务管理是通过TransactionInfo对象来完成的,在该对象中,封装了事务对象的和事务处理的状态信息。
编程式事务
spring编程式事务使用的代码如下:
|
|
在上面的代码中,通过DefaultTransactionDefinition对象持有事务处理属性,在创建事务的过程中得到一个TransactionStatus对象,然后通过transactionManager的commit()和rollback()方法来完成事务的处理。
这段代码是自己写的较简单的事务代码,在上面的invoke()方法中,较详细的使用了编程式事务。
注意:Spring事务默认情况下只对RuntimeException进行回滚。
事务的源码分析
虽然事务的使用有两种方法,但从实用性的角度而言,我们在开发中更多的是使用了声明式事务,下面我们来分析这个步骤:
1、获取事务属性,加载配置中的TransactioManager,不同的事务处理方式使用不同的逻辑。
2、在目标方法执行前获取事务并收集事务信息。
3、执行目标方法(出现异常,尝试异常处理,默认只对RunTimeException和Err回滚),执行成功则提交事务。
4、清除事务信息。
事务的创建
|
|
上面的方法中主要是DelegatingTransactionAttribute封装了传入的TransactionAttribute实例。
获取事务
下面我们看一下获取事务的代码
getTransaction来处理事务的准备工作,
我们具体分析一下上面代码中的操作,事务的操作以DataSourceManager为例:
创建事务实例
|
|
事务设置
|
|
事务的隔离是对数据库连接的设置,在prepareConnectionForTransaction方法
|
|
同步事务信息设置到当前线程
|
|
处理已存在事务
在上一小节中我们分析了新建事务的处理,下面看一下已存在的事务是如何操作的
|
|
记录事务信息
当已经建立事务链接并完成了事务信息的提取之后,我们要将事务信息统一纪录在TransactionInfo的实例中,这个实例记录了目标方法开始前的状态信息,一旦事务执行失败,再根据实例中的信息进行回滚等操作。
|
|
事务回滚
我们在分析事务拦截器的回调方法中可以发现,当目标方法执行之后会出现两种情况:出现异常事务回滚、目标方法执行成功事务提交。下面我们分析一下事务的回滚:
|
|
目标方法执行过程中,出现Throwable的情况就会进入当前方法,但不是所有的Throwable都会被异常处理,只有当异常是RuntimeException和Err的情况下事务才会回滚,其他的异常下数据依旧会被提交。
是否回滚
通过txInfo.transactionAttribute.rollbackOn(ex)来判断当前异常是否需要回滚
|
|
默认情况下只有RuntimeException和Err的的异常才会回滚,但是我们可以扩展来改变。不过更常用的是注解方式来改变异常回滚信息(这里不分析注解的事务,之后会进行分析)。
回滚处理
|
|
回滚后信息清除
|
|
事务提交
上一节我们分析了异常时的事务回滚,下面我们看一下目标方法正常执行后的事务提交:
|
|
|
|
代码的if条件判断两个点:事务状态是否存在保存点与事务是否是新的事务;这两个条件的判断主要是考虑到内嵌事务的情况,内嵌事务开始前设置的保存点在内嵌事务出现异常时方便回滚,如果没有异常,内嵌事务不会单独提交,而是由最外层的事务提交。
事务的提交最终还是由数据库连接进行的操作:
|
|
为了学习,demo如下
TransactionProxyFactoryBean代理
transactionmanager 事务编程式使用
transactiontemplate 事务编程式使用
嵌套事务调用的各种情况