SpringBoot系列:事物加载过程(一)
SpringBoot系列:事物创建过程(二)
SpringBoot系列:事物提交回滚过程(三)
紧接着上节,创建完事物之后,执行目标方法,根据目标方法的执行状态,若有异常进入回滚流程,反之进入提交流程
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
1、事物属性不为null && 判断异常进行回滚,默认是 RuntimeException 以及 Error 满足条件
所以这儿如果没有指定,若抛出的异常不是这两种,则不会回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
1.1、回滚
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 {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
2、不会回滚,进行提交,根据标记的回滚状态TransactionStatus.isRollbackOnly(),
也有可能会发生回滚
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;
}
}
}
}
public final void rollback(TransactionStatus status) throws TransactionException {
1、事物完成的话抛异常
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;
2、处理回滚
processRollback(defStatus, false);
}
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
1、在当前线程所有当前已注册的TransactionSynchronization上触发beforeCompletion方法回调
triggerBeforeCompletion(status);
2、是否存在保存点,回滚到保存点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
3、是否是新的事物,直接回滚当前事物,因事物间的异常是一直往外抛的,所以外层事物可能会全部回滚
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
3.1、不同的管理器不同的实现
doRollback(status);
}
else {
4、到这儿说明没有保存点,也不是新事物,是内层嵌套事物
// Participating in larger transaction
if (status.hasTransaction()) {
5.1、存在事物
isGlobalRollbackOnParticipationFailure:方法意思是若内层事物出现异常,则标记全局回 滚标志,默认是true。
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
5.2、设置事物回滚标志,外层事物捕获到该标记将会回滚
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");
}
// Unexpected rollback only matters here if we're asked to fail early
6、是否标记提前失败,默认是不标记
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
7、在当前线程所有当前已注册的TransactionSynchronization上触发afterCompletion方法回调
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
8、清空一些保存的事物信息,如果存在外层事物,将唤醒挂起的外层事物
cleanupAfterCompletion(status);
}
}
triggerBeforeCompletion
和triggerAfterCompletion
方法是事物提交或回滚的前置前置回调与后置回调,使用如下代码注册
TransactionSynchronizationManager.registerSynchronization(new TestTransactionSynchronization());
。
doSetRollbackOnly
方法标记回滚状态,后续在commit
方法内,根据此标记判断是否需要回滚。
public void rollbackToHeldSavepoint() throws TransactionException {
1、获取保存点信息,包含保存点名称
Object savepoint = getSavepoint();
if (savepoint == null) {
throw new TransactionUsageException(
"Cannot roll back to savepoint - no savepoint associated with current transaction");
}
2、回滚到保存点
getSavepointManager().rollbackToSavepoint(savepoint);
3、释放保存点
getSavepointManager().releaseSavepoint(savepoint);
4、回收spring的保存点为null
setSavepoint(null);
}
这儿是以 DataSourceTransactionManager 为例子
@Override
protected void doRollback(DefaultTransactionStatus status) {
1、事物以及数据库连接信息包装类
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
2、数据库链接信息
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
3、回滚
con.rollback();
}
catch (SQLException ex) {
throw translateException("JDBC rollback", ex);
}
}
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
1、设置事物完成状态
status.setCompleted();
2、如果是新的同步,线程同步器清除threadLocal变量
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
3、如果是新事物
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
4、如果外层事物信息不为null,被挂起了,在这儿唤醒
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
4.1、唤醒事物
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
if (resourcesHolder != null) {
1、获取被挂起的事物资源
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
2、唤醒事物,其实就是将resource资源重新绑定
doResume(transaction, suspendedResources);
}
3、被挂起的事物的同步信息恢复
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
如果存在此前已经被挂起的事务资源,那么这里需要恢复此前的资源,那么将会调用该方法唤醒挂起的事务资源:重新绑定之前挂起的数据库资源,重新唤醒并注册此前的同步器,重新绑定各种事务信息。
怎么恢复此前事务呢?实际上,我们只要了解了“挂起”的概念,那么“恢复”的概念就很简单了,那么什么是“挂起”事务呢?难道真的是对一个连接做了什么操作?实际上并没有什么操作,而且原理真的非常简单。根据此前的源码,首先,我们需要明白,我们每次都只能操作当前绑定的连接对象,操作的连接都是绑定到resources的线程本地变量
。
我们所说的“挂起”事务,实际上就是将绑定到当前线程的连接对象替换为一个新的连接对象,并且被替换的连接对象保存起来
,所以,此后都是操作一个新的连接对象,因为一个连接对应一个事务,那么肯定是一个新的事务了,但这在外人看起来,此前的连接和事务就被像挂起了一样,此前的事务并没有被提交或者回滚完成!
在我们了解了“挂起”的概念之后,那么“恢复”的概念就好知道了,由于此前保存了被“挂起”的资源suspendedResources
,那么在当前事务或者方法完成之后,我们直接将保存的连接资源再次绑定为当前线程正在使用资源即可,也就是绑定到resources线程本地变量
。
到这儿事物的回滚流程结束了
每个事物都会保存旧的事物信息,这儿当前事物结束了,再设置回去,若前一个事务不存在则为null
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
txInfo.restoreThreadLocalStatus();
}
}
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
1、提交入口
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
public final void commit(TransactionStatus status) throws TransactionException {
1、事物状态已经标记完成,抛出异常
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;
2、标记了全局仅仅回滚
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
3、shouldCommitOnGlobalRollbackOnly:对与标记回滚的是否继续提交,默认false
isGlobalRollbackOnly:内部事物标记了回滚,则进行回滚,即使外部事物捕获了异常。
见的传播行为如:内层PROPAGATION_SUPPORTS或者内层PROPAGATION_REQUIRED或者内层PROPAGATION_MANDATORY生效
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
4、回滚
processRollback(defStatus, true);
return;
}
5、提交事物
processCommit(defStatus);
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
1、空实现,提交前的回调
prepareForCommit(status);
2、TransactionSynchronization接口的实现回调
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
3、是否存在保存点,存在则直接释放,因为要准备提交了
与回滚做对比,回滚到保存点之后调用的释放,这儿是直接释放
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
4、是否标记为新事物,提交
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
5、是否标记事物全局回滚提前失败
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.
6、提前失败并抛出 UnexpectedRollbackException 异常
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
6.1、捕获提前失败的异常,执行回调
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
7、出现事物异常
isRollbackOnCommitFailure:是否再提交失败回滚,默认false
执行回调
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
8、如果在执行beforeCompletion回调、回滚保存点、回滚事务等过程中抛出RuntimeException或者Error异常
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
8.1、回滚事物或对事物打上回滚标记
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
9、清理事物资源
cleanupAfterCompletion(status);
}
}
至此,声明式事物整个流程解析完毕,希望您有收获。
流程总结:
TransactionInterceptor
类内进行完成。invoke
准备开始事物,若第一次开启事物设置好一系列如隔离级别、只读、超时等属性,最后根据具体的传播行为开启新事物,若是嵌套事物则根据传播行为做出内嵌套抛出异常或开启新的事物等策略。一些重要知识点:
事物是AOP最好的时间之一,还有就是Spring Cache。
值得注意的内层嵌套事务,在传播行为是 PROPAGATION_NESTED
的情况下,是利用保存点来进行实现savePoint
,归根结底其实还是同一个事物,若发生回滚只需要回滚到保存点,再标记内层事物发生了回滚,再根据具体的策略外层事物做不做真正的回滚还是提交操作。
事物的挂起
和唤醒
,根据一系列的ThreadLocal
变量来完成
1、Spring 一个线程只能绑定一个资源(链接),将当前线程的事物信息保存到TransactionSynchronizationManager & resources
属性上。
2、当发生挂起事物时,当前线程绑定的事物资源将被移除并保存起来,后续将线程绑定到新的事物(可能是空事物)的资源上,并将前一个事物的信息封装到新事物上,所以每一个事物信息上都有可能携带外层事物的信息,就达到了外层事物挂起的目的。
3、事物唤醒时,当前事务处理完毕,观察是否存在外层事物信息,若存在则将外层事物信息重新绑定到线程,以此类推完成嵌套事务恢复的整个流程。