死磕Spring之AOP篇 - Spring 事务详解 (15)

可以看到是通过 DataSourceTransactionManager 提交当前事务

2.1 commit 方法 // AbstractPlatformTransactionManager.java @Override 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()) { // 进行回滚过程(预料之中) processRollback(defStatus, false); return; } // <3> 判断全局回滚时是否需要提交(默认不需要),且当前事务为全局回滚 // 例如 **REQUIRED** 传播级别,当已有一个事务时则加入其中,此时如果抛出异常,则会设置为全局回滚,那么当事务进行提交时,对于整个事务都需要回滚 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { // 进行回滚过程(预料之外) processRollback(defStatus, true); return; } // <4> 执行提交事务 processCommit(defStatus); }

完成事务的过程如下:

如果事务已完成,此时又提交,则抛出异常

事务明确标记为回滚(暂时忽略),则调用 processRollback(..) 方法进行“回滚”

判断全局回滚时是否需要提交(默认不需要),且当前事务为全局回滚,则调用 processRollback(..) 方法进行“回滚”

例如REQUIRED传播级别,当已有一个事务时则加入其中,此时如果抛出异常,则会设置为全局回滚,那么当事务进行提交时,对于整个事务都需要回滚

调用 processCommit(..) 方法,执行提交事务

可以看到并不一定会“提交”,当标记需要全局回滚的状态时会进行“回滚”,这一小节我们重点关注第 4 步

2.2 processCommit 方法

processCommit(..) 方法,执行事务的提交过程,如下:

// AbstractPlatformTransactionManager.java private void processCommit(DefaultTransactionStatus status) throws TransactionException { try { boolean beforeCompletionInvoked = false; try { boolean unexpectedRollback = false; // <1> 进行三个前置操作 prepareForCommit(status); // <1.1> 在 Spring 中为空方法 // <1.2> 调用 TransactionSynchronization#beforeCommit 方法 // 例如在 Mybatis-Spring 中的 SqlSessionSynchronization 会调用其 SqlSession#commit() 方法,提交批量操作,刷新缓存 triggerBeforeCommit(status); // <1.3> 调用 TransactionSynchronization#beforeCompletion 方法 // 由 Spring 事务托管,不是真的关闭连接,从 ThreadLocal 中删除 DataSource 和 ConnectionHolder 的映射关系 // 例如在 Mybatis-Spring 中的 SqlSessionSynchronization 中,会从 ThreadLocal 中删除 SqlSessionFactory 和 SqlSessionHolder 的映射关系, // 且调用其 SqlSession#close() 方法 triggerBeforeCompletion(status); // <2> 标记三个前置操作已完成 beforeCompletionInvoked = true; // <3> 有保存点,即嵌套事务 if (status.hasSavepoint()) { unexpectedRollback = status.isGlobalRollbackOnly(); // <3.1> 释放保存点,等外层的事务进行提交 status.releaseHeldSavepoint(); } // <4> 否则,如果是一个新的事务 else if (status.isNewTransaction()) { unexpectedRollback = status.isGlobalRollbackOnly(); // <4.1> 提交事务,执行 Connection#commit() 方法 doCommit(status); } // <5> 否则,在事务被标记为全局回滚的情况下是否提前失败(默认为 false) 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) { // 如果上面三个前置步骤未完成,调用最后一个前置步骤,即调用 TransactionSynchronization#beforeCompletion 方法 if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } // 提交异常回滚 doRollbackOnCommitException(status, ex); throw ex; } // Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { // <6> 触发提交后的回调,调用 TransactionSynchronization#afterCommit 方法 // JMS 会有相关操作,暂时忽略 triggerAfterCommit(status); } finally { // <7> 触发完成后的回调,事务同步状态为已提交,调用 TransactionSynchronization#afterCompletion 方法 // 例如在 Mybatis-Spring 中的 SqlSessionSynchronization 中,会从 ThreadLocal 中删除 SqlSessionFactory 和 SqlSessionHolder 的映射关系, // 且调用其 SqlSession#close() 方法,解决可能出现的跨线程的情况 triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); } } finally { // <8> 在完成后清理,清理相关资源,“释放”连接,唤醒被挂起的资源 cleanupAfterCompletion(status); } }

提交过程如下:

进行三个前置操作

准备工作,在 Spring 中为空方法

调用 TransactionSynchronization#beforeCommit 方法,例如在 Mybatis-Spring 中的 SqlSessionSynchronization 会调用其 SqlSession#commit() 方法,提交批量操作,刷新缓存

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygsff.html