调用代理类方法时,如果满足切面表达式,会给方法增加事务拦截器链TransactionInterceptor,之后执行该拦截器的invoke()方法。
论述
TransactionInterceptor->invoke():
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
TransactionAspectSupport->invokeWithinTransaction():
// 创建事务,向数据库服务器发送 SET autocommit=0
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); // --1
Object retVal = null;
try {
// 调用下一个拦截器,这里没有,所以是服务类的方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 回滚或提交事务
// 之后向数据库服务器发送 SET autocommit=1
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 提交事务,向数据库服务器发送 COMMIT和 SET autocommit=1
commitTransactionAfterReturning(txInfo);
return retVal;
如果invocation.proceedWithInvocation()出错的话,进入以下逻辑。
TransactionAspectSupport->completeTransactionAfterThrowing():
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
......
}
else {
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
......
}
--1处,在新建事务的时候,决定了数据源与连接对象。
// 新建事务,最终会进入以下方法
DataSourceTransactionManager->doBegin():
// 获得当前数据源的连接对象
Connection newCon = obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
......
// 当前数据源与当前连接对象绑定
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
单事务下的总结
定义异常:@Transactional注解中rollbackFor、rollbackForClassName、noRollbackFor、noRollbackForClassName的属性值
回滚异常:rollbackFor、rollbackForClassName的属性值,异常符合则回滚
非回滚异常:noRollbackFor、noRollbackForClassName的属性值,异常符合则不回滚
运行时异常和系统异常:RuntimeException和Error异常的子类
根据代码以及流程图可知,存在以下两种情况时,事务不会回滚:
- 服务类实例内部调用事务方法,因为这样不会触发AoP代理
- 事务方法的异常未被事务拦截器捕捉到,即在事务方法中try catch而未抛给上层
附录:事务的传播机制
事务的传播:以事务执行A方法的过程中,需要执行B方法,此时B方法也需要以事务的方式运行,那么在创建事务时就会发生事务的传播,与propagation属性值有关,事务创建部分的实现位于AbstractPlatformTransactionManager->getTransaction()方法中
| 事务传播行为类型 | 说明 |
|---|---|
| PROPAGATION_REQUIRED | 默认的事务传播行为,如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中 |
| PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式运行 |
| PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常 |
| PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
| PROPAGATION_NOT_SUPPORTED | 以非事务方式运行操作,如果当前存在事务,就把当前事务挂起 |
| PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常 |
| PROPAGATION_NESTED | 如果当前没有事务,新建一个事务。如果当前存在事务,则在该事务内作为子事务执行。可以实现嵌套事务回滚而外部事务不回滚,原理是savepoint,如果连接器不支持嵌套事务,会报错 |
总结:
- 一个线程最多存在一个活动的事务,如果新建事务,会将当前事务挂起
- 事务的个数小于等于事务的方法数,即使事务共用,一个事务方法也会完整地执行一次
TransactionAspectSupport->invokeWithinTransaction()方法
举例:以下两个事务方法共用一个事务
@Transactional
Demo->test():
student.test();
@Transactional
Student->test():
dosomething();