1.简介
事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP实现,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。本文将着重介绍基于 @Transactional 注解的事务管理。
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
注:新版本源码中提到,在Spring6.0版本之后将支持protected
权限的事务方法。
2.回滚机制
2.1.简述
Spring的事务管理默认是针对unchecked exception回滚,也就是默认对Error异常和RuntimeException异常以及其子类进行事务回滚,且必须抛出异常,若使用try-catch对其异常捕获则不会进行回滚(Error异常和RuntimeException异常抛出时不需要方法调用throws或try-catch语句);而checked exception 则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它,所以必须在service捕获异常,然后再次抛出,这样事务方才起效。
当然,Spring也为我们提供了注解属性来解决这些问题,在@Transaction注解中的rollbackFor属性中可以指定需要回滚的异常类型,Sprign在事务处理底层根据这些配置来进行相应的处理,就可以实现回滚我们自定义的异常类型。rollbackFor属性可以指定单个异常类型,也可以指定多个异常类型。
2.2.Java中的异常体系简介
Java标准库内建了一些通用的异常,这些类以 Throwable 为顶层父类。 Throwable又派生出 Error 类和 Exception 类:
-
错误(Error):Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。
-
异常(Exception):Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
总体上我们根据Javac对异常的处理要求,将异常类分为2类:
-
非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。
-
检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
需要明确的是:检查和非检查是对于javac来说的,这样就很好理解和区分了。
Spring默认会回滚上图中的Error和RuntimeException,IOException就不会回滚,需要手动指定。
3.3.声明式事务和编程式事务
Sping 中事务的操作用两种:
-
编程式事务(手写代码操作事务)
-
声明式事务(利用注解自动开启和提交事务)
编程式事务
Spring内置了两个对象,DataSourceTransactionManager (事务管理器)用来获取事务(开启事务)、提交或回滚事务的,而 TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从而获得一个事务 TransactionStatus 对象。
@RestController
@RequestMapping("/test")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private DataSourceTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
@RequestMapping("/insert")
public int insert(User user) {
// 开启事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
// 添加数据
int res = userService.add(user);
System.out.println("新增数据行数:" + result);
// 提交事务
transactionManager.commit(transactionStatus);
// 回滚事务
// .....发生异常的时候
transactionManager.rollback(transactionStatus);
return res;
}
}
运行程序分别查看提交事务和回滚事务的效果即可。
声明式事务
声明式事务的实现,只需要在方法上添加 @Transactional 注解就可以实现,无需手动开启事务和提交事务,进入方法时自动开启事务,方法执行完全会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务(如果需要处理非检查异常则需要指定rollbackFor的异常类型)。
3.使用方法
3.1.简单使用
注:SpringBoot项目会自动配置一个 DataSourceTransactionManager事务管理器,所以我们只需在方法(或者类)加上 @Transactional 注解,就自动纳入 Spring 的事务管理了。并且只做声明式事务案例,编程式事务使用较少。
如下在方法加上 @Transactional 注解:
@Transactional
public void insertUser() {
User user = new User("张三");
//向数据库插入一条记录
userMapper.insertUser(user);
//手动模拟抛出异常以触发回滚
throw new RuntimeException("发生异常");
}
抛出异常之后,事务会自动回滚,数据不会插入到数据库,使用起来很简单也是声明式事务的特性。
3.2.@Transaction注解解析
话不多说,先看看该注解定义:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
从上面看出@Transactional
既可以作用于类上,也可以作用于方法上,作用于类:表示所有该类的public
方法都配置相同的事务属性信息。接下来再看看其属性:
propagation: 设置事务的传播行为,主要解决是A方法调用B方法时,事务的传播方式问题的,默认值为 Propagation.REQUIRED
,其他属性值信息如下:
事务传播行为 | 解释 |
---|---|
REQUIRED(默认值) | A调用B,B需要事务,如果A有事务B就加入A的事务中,如果A没有事务,B就自己创建一个事务。 |
REQUIRED_NEW | A调用B,B需要新事务,如果A有事务就挂起,B自己创建一个新的事务。 |
SUPPORTS | A调用B,B有无事务无所谓,A有事务就加入到A事务中,A无事务B就以非事务方式执行。 |
NOT_SUPPORTS | A调用B,B以无事务方式执行,A如有事务则挂起。 |
NEVER | A调用B,B以无事务方式执行,A如有事务则抛出异常。 |
MANDATORY | A调用B,B要加入A的事务中,如果A无事务就抛出异常。 |
NESTED | A调用B,B创建一个新事务,A有事务就作为嵌套事务存在,A没事务就以创建的新事务执行。 |
isolation :事务的隔离级别,默认值为 Isolation.DEFAULT
。指定事务的隔离级别,事务并发存在三大问题:脏读、不可重复读、幻读/虚读。可以通过设置事务的隔离级别来保证并发问题的出现,常用的是READ_COMMITTED 和REPEATABLE_READ,也就是读已提交和可重复读。
isolation属性 | 解释 |
---|---|
DEFAULT | 默认隔离级别,取决于当前数据库隔离级别,例如MySQL默认隔离级别是REPEATABLE_READ |
READ_UNCOMMITTED | A事务可以读取到B事务尚未提交的事务记录,不能解决任何并发问题,安全性最低,性能最高 |
READ_COMMITTED | A事务只能读取到其他事务已经提交的记录,不能读取到未提交的记录。可以解决脏读问题,但是不能解决不可重复读和幻读 |
REPEATABLE_READ | A事务多次从数据库读取某条记录结果一致,可以解决不可重复读,不可以解决幻读 |
SERIALIZABLE | 串行化,可以解决任何并发问题,安全性最高,但是性能最低 |
timeout :事务的超时时间,默认值为 -1(永不超时)。如果配置了超时时间(时间以秒为单位)并且超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly:指定事务是否为只读事务,默认值为 false,为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true以提高性能。
rollbackFor:用于指定能够触发事务回滚的异常类型,可以指定多个异常类型也可以指定单个异常类型。
noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
3.3.@Transaction事务失效场景
3.3.1.数据库不支持事务
最基本的,我们要向想事务生效,所操作的数据库底层一定要先支持事务操作。MySQL举例,必须使用InnoDB引擎才能够有效地支持事务,如果是MyISAM引擎则无法支持事务操作。
3.3.2.方法访问权限问题
众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。但如果我们在开发过程中,把有某些事务方法,定义了错误的访问权限,就会导致事务功能出问题,例如:
@Service
public class UserService {
@Transactional
private void add(UserModel userModel) {
saveData(userModel);
updateData(userModel);
}
}
我们可以看到add方法的访问权限被定义成了private
,这样会导致事务失效,spring要求被代理方法必须是public
的,并且以上代码在IDE工具里面会提示编译错误不通过。在Spring事务实现源码AbstractFallbackTransactionAttributeSource
类的computeTransactionAttribute
方法中有个判断,如果目标方法不是public,则TransactionAttribute
返回null,即不支持事务(后面详解事务实现原理)。
3.3.3.方法用final修饰
有时候,某个方法不想被子类重新,这时可以将该方法定义成final的。普通方法这样定义是没问题的,但如果将事务方法定义成final,例如:
@Service
public class UserService {
@Transactional
public final void add(UserModel userModel){
saveData(userModel);
updateData(userModel);
}
}
我们可以看到add方法被定义成了final
的,这样会导致事务失效。
为什么?
因为在spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。
注意:如果某个方法是static的,同样无法通过动态代理,变成事务方法。
3.3.4.方法内部调用
有时候我们需要在某个Service类的某个方法中,调用另外一个事务方法,比如:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(UserModel userModel) {
userMapper.insertUser(userModel);
updateStatus(userModel);
}
@Transactional
public void updateStatus(UserModel userModel) {
doSameThing();
}
}
简而言之:在同一个类中,一个普通方法调用事务方法会直接导致事务失效,两个方法添加的数据都没有被回滚。但是两个事务方法之间调用可以正常回滚,就是在add方法上也加上@Transactional注解。反之,如果在同一个类中事务方法调用普通的方法也是可以回滚的,前提是普通方法必须是public。
3.3.5.多线程调用
在实际项目开发中,多线程的使用场景还是挺多的。如果spring事务用在多线程场景中,会有问题:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional
public void add(UserModel userModel) throws Exception {
userMapper.insertUser(userModel);
new Thread(() -> {
roleService.doOtherThing();
}).start();
}
}
@Service
public class RoleService {
@Transactional
public void doOtherThing() {
System.out.println("保存role表数据");
}
}
从上面的例子中,我们可以看到事务方法add中,调用了事务方法doOtherThing,但是事务方法doOtherThing是在另外一个线程中调用的。这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,add方法也回滚是不可能的。在spring事务源码中,spring的事务是通过数据库连接来实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
3.3.6.异常被try-catch捕获
如下图所示:
@Transactional(rollbackFor = Exception.class)
public void addUser(UserParam param) {
try {
User user = PtcBeanUtils.copy(param, User.class);
// 添加用户角色
this.addUserRole(user.getId(), param.getRoleIds());
log.info("执行结束了");
} catch (Exception e) {
log.error(e.getMessage());
}
}
@Transactional(rollbackFor = Exception.class)
public void addUserRole(Long userId, List<Long> roleIds) {
List<UserRole> userRoles = new ArrayList<>();
roleIds.forEach(roleId -> {
UserRole userRole = new UserRole();
userRole.setUserId(userId);
userRole.setRoleId(roleId);
userRoles.add(userRole);
});
userRoleDAO.insertBatch(userRoles);
throw new RuntimeException("发生异常");
}
@Transactional
事务生效原则:只有异常传播出了标记了 @Transactional 注解的方法,事务才能回滚。因为Spring的事务通过aop动态代理执行目标方法,通过切点对目标方法的增强处理来控制事务,所以必须在执行方法的过程中“感知”到异常才能被正确处理,如果只是简单捕获异常并没有向外抛出,Spring感觉不到异常信息就会认为没有异常进而提交事务导致了事务失效。
3.3.7.rollbackFor设置错误
直接看代码:
@Transactional
public void addUser(UserParam param) {
User user = PtcBeanUtils.copy(param, User.class);
.......
// 添加用户角色
this.addUserRole(user.getId(), param.getRoleIds());
log.info("执行结束了");
}
public void addUserRole(Long userId, List<Long> roleIds) throws Exception {
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
List<UserRole> userRoles = new ArrayList<>();
roleIds.forEach(roleId -> {
UserRole userRole = new UserRole();
userRole.setUserId(userId);
userRole.setRoleId(roleId);
userRoles.add(userRole);
});
userRoleDAO.insertBatch(userRoles);
throw new Exception("发生异常");
}
这里#addUser()
使用@transactional
,但没有设置rollbackFor
属性,且#addUserRole()
抛出的异常是exception
,不是RuntimeException
,这样事务也失效了,因为默认情况下,出现 RuntimeException(非受检异常)或 Error 的时候,Spring才会回滚事务,不指定其他异常类型则不会回滚。
3.4.长事务导致的问题
在开发中经常会觉得Spring的声明式事务使用非常简单,即@Transactional
,所以从来不注重细节。当 Spring 遇到该注解时,会自动从数据库连接池中获取 connection,并开启事务然后绑定到 ThreadLocal 上,对于@Transactional注解包裹的整个方法都是使用同一个connection连接。如果我们出现了耗时的操作,比如第三方接口调用、业务逻辑复杂、大批量数据处理等就会导致我们我们占用这个connection的时间会很长,数据库连接一直被占用不释放。一旦类似操作过多,就会导致数据库连接池耗尽。这就是典型的长事务问题
长事务引发的常见危害有:
-
数据库连接池被占满,应用无法获取连接资源;
-
容易引发数据库死锁;
-
数据库回滚时间长;
-
在主从架构中会导致主从延时变大。
服务系统开始出现故障:数据库监控平台一直收到告警短信,数据库连接不足,出现大量死锁;日志显示调用流程引擎接口出现大量超时;同时一直提示CannotGetJdbcConnectionException
,数据库连接池连接占满。
要想解决这中问题其实也不难,只需要对方法进行拆分,将不需要事务管理的逻辑与事务操作分开,这样就可以有效控制事务的时长从而避免长事务。
4.Transactional的工作原理
4.1.工作机制简述
先来看下官方的事务简图:
Spring中的@Transaction注解的工作流程是基于AOP实现,基于AbstractBeanFactoryPointcutAdvisor、StaticMethodMatcherPointcut、MethodInterceptor的aop编程模式,增强了标注有@Transaction注解的方法(跟我们平时使用注解在切面中对目标方法进行处理是同一个道理)。并且同时抽象了事务行为PlatformTransactionManager(事务管理器)、TransactionStatus(事务状态)、TransactionDefinition(事务定义)等形态,最终将事务的开启、提交、回滚等逻辑嵌入到被增强的方法的前后,完成统一的事务模型管理。
4.2.事务aop核心类
注:这里的集合核心类只做解释,不按执行流程进行排序。
@Transactional(上面已经简介过了)
事务注解,用于定位aop的切入点,事务注解里包含了完整事务的所有基本属性,常见的属性如:
-
transactionManager:事务管理器
-
propagation:传播行为定义,枚举类型,是spring独有的事务行为设计,默认为PROPAGATION_REQUIRED(支持当前事务,不存在则新建)
-
isolation:隔离级别,对应数据库的隔离级别实现,mysql默认的隔离级别是 read-committed
-
timeout:超时时间,默认使用数据库的超时,mysql默认的事务等待超时为5分钟
-
readOnly:是否只读,默认是false
-
rollbackFor:异常回滚列表,默认的是RuntimeException异常回滚
TransactionAttribute
这是一个Spring定义的接口,承载了@Transactional注解里的所有属性,实现类的继承关系如下类结构图,这个实例在被注解解析器创建好后,会在事务上下文中传递。
接口是一种抽象,由于RuleBasedTransactionAttribute
类继承了DefaultTransactionAttribute
类,该类又实现了TransactionAttribute接口,所以在实际使用过程中@Transaction注解中的所有属性最终会被解析并封装在RuleBasedTransactionAttribute
类中返回,解析的过程由SpringTransactionAnnotationParser
类的parseTransactionAnnotation
方法实现。
SpringTransactionAnnotationParser
见名知意,这个类是Spring的事务注解解析器,这个类实现自TransactionAnnotationParser
接口(Spring中解析事务注解的顶层接口),是spring管理的事务解析器,用于解析@Transactional注解,将注解里的属性设置到TransactionAttribute
的实现类属性里。除了这个,另还有两个实现,分别是JTA事务注解解析器,和EJB事务注解管理解析器,区别是解析的注解不同,Spring是@Transactional,jta是javax.transaction.Transactional,EJB是javax.ejb.TransactionAttribute。关键代码如下,通过AnnotatedElementUtils类,这个类在spring-core包下,找到注解属性集AnnotationAttributes,如果不为空,则包装成事务属性集返回:
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element,Transactional.class,false,false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
} else {
return null;
}
}
AnnotationTransactionAttributeSource
见名知意,这个类是注解事务属性集的源,简而言之:Spring抽象了获取事务注解属性集的行为,而AnnotationTransactionAttributeSource
正是收集@Transactional事务注解属性集的具体实现。上面的SpringTransactionAnnotationParser
就是在这个类中用到的,用于发现@Transactiona注解中的属性。就是在执行目标事务方法的时候通过aop切点,执行事务处理逻辑时获取事务的TransactionAttribute
调用到该类及其方法。
TransactionInterceptor
事务的拦截器。aop编程里,有了切入点Pointcut,就要有通知advice,我们熟悉的spring aop里有前置、后置、环绕、异常等通知类型,TransactionInterceptor属于自定义通知模型实现,实现自Advice接口,类似于环绕通知,具体见类结构图,如下:
核心方法如下:
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
被@Transactional注解的方法,如果被aop正确的增强了,运行的时候都会进入到这个方法里面,如果你发现事务不生效等等问题,可以从这里开始定位真实原因。也就是说@Transaction 事务注解执行流程是通过Spring的aop切入点机制,然后从这个方法中开始执行所有的事务处理流程(start、commit、rollback事务)。
BeanFactoryTransactionAttributeSourceAdvisor
事务增强器,用于增强添加了@Transactional注解的方法,上面提到的这些核心类,最终都作用于这里,用于寻找@Transactional注解的方法和织入事务处理逻辑。换句话说就是加了@Transaction事务注解的方法都是通过该类的相关配置来实现切入点处理的,也就是有了这个类事务的所有处理才能通过切点去执行。
PlatformTransactionManager
平台事务管理器(就是事务管理器),这是Spring事务基础设施中的中心接口。它定义了三个最最基本的事务方法,getTransaction获取事务,包含了事务开启的行为,commit提交事务,rollback回滚事务。代码如下:
public interface PlatformTransactionManager extends TransactionManager {
// 获取事务(包含了开启一个事务)
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException;
// 提交事务
void commit(TransactionStatus status) throws TransactionException;
// 回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
在spring-tx模块中并没有提供真正的事务管理器实现类,只提供了一个抽象派生类AbstractPlatformTransactionManager,并建议其他具体的事务管理器实现基于这个派生类,因为它预先实现了定义的传播行为并处理事务同步处理。子类必须为底层事务的特定状态实现该抽象派生类中的模板方法,例如:begin、commit、rollback等。我们平时常见的实现有:JpaTransactionManager、JtaTransactionManager、DataSourceTransactionManager(mybatis用的就是这个事务管理器)等。
事务管理器和事务aop处理的逻辑本身没有任何耦合,只需将PlatformTransactionManager实例注册到spring上下文中即可,事务拦截器会通过获取到@Transactional里的transactionManager属性去上下文中寻找事务管理器,并将其缓存起来,见TransactionAspectSupport.java里的determineTransactionManager方法。
TransactionStatus
事务状态的抽象接口,用这个接口的实现类来维护当前的事务状态,spring-tx里提供了默认的实现DefaultTransactionStatus
。一般情况下这个不需要我们关心,它和PlatformTransactionManager
是成对存在的,仔细看不难发现,PlatformTransactionManager
里的三个事务行为传递的就是TransactionStatus
。我们知道事务aop增强了添加@Transactional的方法,在执行方法前调用PlatformTransactionManager.getTransaction
开启事务并返回一个TransactionStatus
,之后调用commit方法提交事务,提交事务的入参TransactionStatus
就是开启事务时获得的。参见TransactionAspectSupport
.java里的createTransactionIfNecessary
方法(该方法就是在切点拦截到目标方法执行的时候,处理事务相关逻辑时开启一个新的事务)。
TransactionDefinition
事务定义,对应了TransactionAttribute
,因为TransactionAttribute
继承了TransactionDefinition
,最终通过aop得到的TransactionAttribute
里的属性会被传递到TransactionDefinition
里(TransactionAttribute 和 TransactionDefinition都是接口,实际实现都是由实现类或实现类的子类实现),所以TransactionDefinition
里也包含了所有事务相关的属性,PlatformTransactionManager.getTransaction
正是通过这个里面的属性去获取的事务。AbstractPlatformTransactionManager
派生类里也是通过TransactionDefinition
这个里面的属性去判断协调spring的事务传播行为的。
4.3.执行流程及原理
4.3.1.整体事务执行流程
Spring中的事务处理用到的AOP动态代理以及切面编程,既然有切面/切入点就会有通知,当拦截到目标方法执行的时候就会调用拦截接口的invoke
方法,去执行目标方法和事务处理逻辑,这里是用的Spring中MethodInterceptor
拦截接口,话不多说直接上代码:
1.拦截到目标方法之后,调用拦截接口的invoke方法
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
// 该方法在TransactionInterceptor类中,这个拦截器实现了MethodInterceptor,重写了invoke方法。
这里方法参数的MethodInvocation
就是对目标方法调用的描述,专门用于传递给拦截器。通过AopUtils.getTargetClass
获取到目标类中的类信息返回Class对象,最终执行对事务的处理逻辑,也就是我们常说的对目标方法的增强。
2.执行内部事务
接下来看调用的invokeWithinTransaction
方法的内部究竟是对事务流程做了怎样的处理(该方法在TransactionAspectSupport
拦截器类中),直接上代码:
// ---------------------------------------------------------------代码1--------------------------------------------------------------- //
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 如果事务属性为空,则该方法是非事务性的。
// 这里首先获取TransactionAttribute,上面也提过了它是封装了@Transaction注解的属性集
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 这里确定要为给定事务使用的特定事务管理器,也就是获取事务管理器(详见代码2)
final TransactionManager tm = determineTransactionManager(txAttr);
// 这个if是给反应式编程模型中的事务做处理,暂时略过
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
}
// 把刚获取的事务管理器转换成PlatformTransactionManager
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 这里获取当前执行的目标事务方法的全限定名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 如果事务属性为空或者当前事务管理器不是CallbackPreferringPlatformTransactionManager
// CallbackPreferringPlatformTransactionManager是相对少见的事务管理器,他支持通过回调机制来执行事务操作
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 这里就是开启一个事务
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
// 目标事务方法的执行结果
Object retVal;
try {
// 执行目标事务方法,很像切面中的@Around环绕通知
retVal = invocation.proceedWithInvocation();
} catch (Throwable ex) {
// 目标调用异常后进行回滚(这里就可以看出,catch的是Throwable异常的顶层父类,所以目标方法如果被try-catch捕获异常而不抛出,则不能回滚事务)
completeTransactionAfterThrowing(txInfo, ex); // completeTransactionAfterThrowing逻辑详见代码3
throw ex;
} finally {
// 清理事务相关资源(例如ThreadLocal中绑定的当前线程中的事务信息等)
cleanupTransactionInfo(txInfo);
}
// 这里是判断是否引入了 Vavr库,并且目标方法的返回结果是否Vavr 的 Try 对象
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
// 在Vavr失败匹配回滚规则的情况下设置只回滚…
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 执行提交事务(逻辑详见代码4)
commitTransactionAfterReturning(txInfo);
// 返回目标事务方法的执行结果
return retVal;
} else {
// 当事务管理器是CallbackPreferringPlatformTransactionManager,允许回调机制来执行事务操作走以下代码
final ThrowableHolder throwableHolder = new ThrowableHolder();
// 它是一个CallbackPreferringPlatformTransactionManager: 传入一个TransactionCallback。
try {
Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// 在Vavr失败匹配回滚规则的情况下设置为只回滚…
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
} catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
} else {
throw new ThrowableHolderException(ex);
}
} else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
} finally {
cleanupTransactionInfo(txInfo);
}
});
// 检查结果状态:它可能指示要重新抛出的Throwable。
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
} catch (ThrowableHolderException ex) {
throw ex.getCause();
} catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
} catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
// ---------------------------------------------------------------代码2--------------------------------------------------------------- //
@Nullable
protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
// 如果没有设置tx属性,不要尝试查找tx管理器
if (txAttr == null || this.beanFactory == null) {
// 返回在配置中设置的默认事务管理器 ,如果没有则返回null
return getTransactionManager();
}
// 与当前事务关联的限定符
String qualifier = txAttr.getQualifier();
// 这两个判断都是通过限定符去get现有的事务管理器
if (StringUtils.hasText(qualifier)) {
return determineQualifiedTransactionManager(this.beanFactory, qualifier);
} else if (StringUtils.hasText(this.transactionManagerBeanName)) {
return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
} else {
// 如果都没有就获取默认的事务管理器
TransactionManager defaultTransactionManager = getTransactionManager();
// 如果没有配置事务管理器
if (defaultTransactionManager == null) {
// 从事务管理器缓存当中取(这个缓存是一个线程安全的CurrentHashMap)
defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
// 如果依然没有
if (defaultTransactionManager == null) {
// 那就通过IOC的对象实例工厂去获取
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
// 获取到后put进事务管理器缓存中
this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
// 返回事务管理器
return defaultTransactionManager;
}
}
// ---------------------------------------------------------------代码3--------------------------------------------------------------- //
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
// 事务info和事务的Status不为null
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 判断是否打印日志
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
}
// 事务属性不为空,并且当前的异常类型需要回滚
// txInfo.transactionAttribute.rollbackOn(ex)就是判断当前异常类型是否回滚,前面提到默认只会回滚非检查异常:RuntimeException和Error,如果
// 未指定异常类型,在发生受检异常之后不会进入if而进入else提交事务,这就是rollbackFor指定回滚异常类型的作用。
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 获取到事务管理器然后通过事务管理器回滚事务
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
} catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
} else {
// 我们不回滚这个异常。
// 如果TransactionStatus.isRollbackOnly()为true,仍然会回滚。
try {
// 获取到事务管理器然后通过事务管理器提交事务
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
} catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
} catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
// ---------------------------------------------------------------代码4--------------------------------------------------------------- //
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
// 事务info和事务状态对象不为null
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 判断是否打印日志
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// 获取到事务管理器通过事务管理器提交当前事务
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
至此,基本的事务处理执行流程就总结完了,简单小结一下:
-
通过AOP 动态代理和切面拦截处理标记了@Transaction注解的方法。
-
进入
TransactionInterceptor
事务拦截器,执行invoke
方法,调用TransactionAspectSupport.invokeWithinTransaction
方法。 -
获取事务所有的属性资源,获取事务管理器。判断是否是反应式编程模型中的事务处理。
-
创建并开启一个事务,执行目标事务方法。
-
发生异常则回滚,需要判断当前异常类型是否回滚。未发生异常则直接提交当前事务。
-
清理事务相关资源。
4.3.2.事务开启流程
那么整体流程总结完之后,具体的实现细节又是什么样的呢?开启事务的过程做了什么?提交或回滚的过程都做了什么?接下来继续详细分析,首先先从开启一个事务说起。代码1中的TransactionSupport.invokeWithinTransaction
方法中事务处理做了开启一个新的事务,方法名叫createTransactionIfNecessary
,点进去这个方法看内部实现细节(全部解释都在代码注释中):
注:所有事务的开始、提交、回滚的详细实现,都在各自不同的数据库持久层框架的具体实现中,例如mybatis
就是DataSourceTransactionManager
,在使用中的实际类型是JdbcTransactionManager
,他继承了DataSourceTransactionManager
。
// ---------------------------------------------------------------代码5--------------------------------------------------------------- //
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// 如果未指定事务的名称,则应用方法标识作为事务名称。
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
// 事务状态
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取事务信息返回TransactionStatus
status = tm.getTransaction(txAttr); // 详见代码6
} else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 为给定的事务属性和事务状态对象准备一个TransactionInfo。
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
// ---------------------------------------------------------------代码6--------------------------------------------------------------- //
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
// Use defaults if no transaction definition given.
// 如果没有给定的事务定义,则使用默认值。
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 获取事务
Object transaction = doGetTransaction(); // 详见代码7
boolean debugEnabled = logger.isDebugEnabled();
// 判断是否有现有的事务
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 找到现有的事务 -> 检查传播属性行为以了解其行为方式。
return handleExistingTransaction(def, transaction, debugEnabled);
}
// Check definition settings for new transaction.
// 检查新事务的定义设置(防止出现非法的事务超时时间,默认是-1,不能小于-1)
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
// 未找到现有事务 -> 检查传播属性行为以了解其行为方式。
// TransactionDefinition.PROPAGATION_MANDATORY 该传播属性意为如果没有已存在的事务则抛出异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
} else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 挂起给定的事务。首先挂起事务同步,然后委托给 doSuspend 模板方法。
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 开启事务(详见代码8)
return startTransaction(def, transaction, debugEnabled, suspendedResources);
} catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
} else {
// 创建“空”事务: 没有实际的事务,但可能同步。
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
// ---------------------------------------------------------------代码7--------------------------------------------------------------- //
@Override
protected Object doGetTransaction() {
// 创建数据源对象
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允许事务嵌套,默认是false
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 获取当前持有的数据库连接
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// 将持有的数据库连接set进数据源对象
txObject.setConnectionHolder(conHolder, false);
// 返回
return txObject;
}
// ---------------------------------------------------------------代码8--------------------------------------------------------------- //
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 通过给定参数创建一个TransactionStatus实例
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启新的事务(详见代码9)
doBegin(transaction, definition);
// 根据需要(事务状态和事务定义)初始化事务同步,这里就设置了事务的名称、事务隔离级别、是否为只读事务、当前的事务同步等信息
prepareSynchronization(status, definition);
return status;
}
// ---------------------------------------------------------------代码9--------------------------------------------------------------- //
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
// 事务数据源对象
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 是否有连接持有或者资源是否与同步事务
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// obtainDataSource()通过当前的DateSource获取当前数据库连接
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 创建新的事务持有(参数是当前数据库连接)set进事务数据源对象
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 设置资源与同步事务
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
// 数据库连接
con = txObject.getConnectionHolder().getConnection();
// 通过当前数据库连接和事务定义获取事务隔离级别(具体逻辑就是事务定义不为空并且是只读事务,就把当前数据库连接设置为只读事务;把当前数据库连接的事 务隔离级别设置为事务定义中的隔离级别)
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
// 给事务数据源对象设置隔离级别
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// 给事务数据源对象设置是否只读事务
txObject.setReadOnly(definition.isReadOnly());
/*
如有必要,切换到手动提交。这在一些 JDBC 驱动程序中代价较高,
因此,我们不想不必要地这样做(例如,如果我们显式地配置连接池以设置它)。
*/
// 数据库连接是否是自动提交事务(默认自动提交)
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
// 设置为手动提交事务
con.setAutoCommit(false);
}
// 如果是只读事务,通过JDBC执行sql设置为只读事务:SET TRANSACTION READ ONLY
prepareTransactionalConnection(con, definition);
// 标注当前事务处于活动状态
txObject.getConnectionHolder().setTransactionActive(true);
// 获取事务注解中的超时时间设置,未设置则返回-1
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
// 设置超时时间
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 将当前连接持有绑定到线程上
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
} catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
至此,事务的开启流程就解析完成。
4.3.3.事务提交流程
事务提交的流程同样也是在代码1中的TransactionSupport.invokeWithinTransaction
方法中事务处理逻辑执行完目标事务方法之后未发生异常,就执行事务提交逻辑,方法名是commitTransactionAfterReturning
,接下来看事务提交流程:
// ---------------------------------------------------------------代码10--------------------------------------------------------------- //
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// 没有多余逻辑直接提交事务(详见代码11)
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
// ---------------------------------------------------------------代码11--------------------------------------------------------------- //
@Override
public final void commit(TransactionStatus status) throws TransactionException {
// 事务是否已经完成
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
// 事务状态
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 是否标记强制回滚(这里就是代码1中提到的是否引入了 Vavr库,在Vavr失败匹配回滚规则的情况下设置只回滚)
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
// 执行回滚;逻辑
processRollback(defStatus, false);
return;
}
// 检查当前事务管理器是否允许在全局事务标记为回滚-only 的情况下提交事务,并且被标记为全局回滚-only
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
// 提交事务(详见代码12)
processCommit(defStatus);
}
// ---------------------------------------------------------------代码12--------------------------------------------------------------- //
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
// 在事务提交之前触发的操作,可以在业务逻辑中通过TransactionSynchronization.registerSynchronization方法写入自定义业务逻辑
triggerBeforeCommit(status);
// 在事务完成之前触发的操作
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
// 如果有事务保存点则释放事务保存点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
} else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 提交事务(详见代码13)
doCommit(status);
} else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
} catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
} catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
} catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
// 事务提交后触发的操作,注意:这里的操作是在事务已提交之后执行,所以无论什么逻辑都不能实现业务回滚这类操作
try {
triggerAfterCommit(status);
} finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
} finally {
cleanupAfterCompletion(status);
}
}
// ---------------------------------------------------------------代码13--------------------------------------------------------------- //
@Override
protected void doCommit(DefaultTransactionStatus status) {
// 事务数据源对象
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
// 数据库连接对象
Connection con = txObject.getConnectionHolder().getConnection();
// 是否打印日志
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
// 调用底层的JDBC的Connection提交事务
con.commit();
} catch (SQLException ex) {
throw translateException("JDBC commit", ex);
}
}
至此,事务的提交流程就解析完成。
4.3.4.事务回滚流程
事务回滚的流程在代码3中的txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
这行代码中,事务处理逻辑执行完目标事务方法之后发生异常,就执行事务回滚逻辑,方法名是completeTransactionAfterThrowing
,接下来看事务回滚流程:
// ---------------------------------------------------------------代码14--------------------------------------------------------------- //
@Override
public final void rollback(TransactionStatus status) throws TransactionException {
// 事务已经完成则抛出异常
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 执行回滚事务(详见代码15)
processRollback(defStatus, false);
}
// ---------------------------------------------------------------代码15--------------------------------------------------------------- //
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 在事务回滚之前触发的操作
triggerBeforeCompletion(status);
// 如果有事务保存点则释放事务保存点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
} else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
// 执行回滚事务(详见代码16)
doRollback(status);
} else {
// 参与一些较大的事务,如果有现有的事务则标记为强制回滚状态
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
} else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
} else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// 意外回滚只有在我们被要求提前失败时才有意义
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
} catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 在事务回滚之后触发的操作
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// 如果我们有一个全局回滚标记,引发unexpected drollbackexception
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
} finally {
// 清理事务同步等相关资源
cleanupAfterCompletion(status);
}
}
// ---------------------------------------------------------------代码16--------------------------------------------------------------- //
@Override
protected void doRollback(DefaultTransactionStatus status) {
// 事务数据源对象
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
// 数据库连接对象
Connection con = txObject.getConnectionHolder().getConnection();
// 是否打印日志
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
// 调用底层的JDBC的Connection回滚事务
con.rollback();
} catch (SQLException ex) {
throw translateException("JDBC rollback", ex);
}
}
至此,事务的回滚流程就解析完成。
关于Spring的事务体系还有很多很丰富的实现,本文只是略微把事务的基本执行原理进行分析,例如事务的同步处理,与当前线程的绑定,多种不同持久层的具体实现,以及自定义配置的具体实现等等,就待以后再来学习吧。
PS:本文是基于参考网络博客及自己梳理Spring-tx模块中的部分源码整理的学习笔记,如有不当之处还请各位大佬不吝指出,感激不尽~~