Spring 源码分析之事务
Spring 源码分析之事务2 TransactionStatus与TransactionInfo
Spring 源码分析之事务3 事务的提交与回滚
我们回顾一下事务处理的入口逻辑,代码如下:
protected Object invokeWithinTransaction(Method method, @Nullable Class> targetClass,
final InvocationCallback invocation) throws Throwable {
......
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//一:创建事务
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
//二:调用业务:
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
//三:目标方法发生异常的处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//四:恢复当前的事务信息对象也就是TransactionInfo
cleanupTransactionInfo(txInfo);
}
......
//五:提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
......
}
今天我们重点关注第三、四、五步所到的事情。
一、第三步目标方法发生异常的处理completeTransactionAfterThrowing(txInfo, ex)
这里的业务逻辑比较简单:主要就是判断目标发生发生的异常类型是不是RuntimeException || ex instanceof Error,如果是就回滚事务,如果不是,则继续提交事务。
源码如下:
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
//如果txInfo.transactionAttribute.rollbackOn(ex)为true,就回滚,否则进入else语块,执行commit操作
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
......
}
}
txInfo.transactionAttribute.rollbackOn(ex)源码如下:
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
二、第四步恢复当前的事务信息对象也就是TransactionInfo,cleanupTransactionInfo()
不管目标方法执行是否正常不管抛不抛异常,都进入finally,恢复TransactionInfo
源码如下:
/**
* Reset the TransactionInfo ThreadLocal.
* Call this in all cases: exception or normal return!
* @param txInfo information about the current transaction (may be {@code null})
*/
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
txInfo.restoreThreadLocalStatus();
}
}
这个时候调用的就是当前TransactionInfo 的restoreThreadLocalStatus方法。源码如下:
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}
不清楚TransactionInfo 的可以看看我写的spring源码分析之事务2
三、第五步提交事务commitTransactionAfterReturning(txInfo);
如果目标方法执行没有问题,则会进入事务的最后一个阶段:提交事务。
commitTransactionAfterReturning()逻辑如下,看到commit方法我们就放心了:
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
查看commit源码
@Override
public final void commit(TransactionStatus status) throws TransactionException {
......
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
//一、如果当前事务设置了回滚,那么这里就执行事务的回滚操作。
if (defStatus.isLocalRollbackOnly()) {
processRollback(defStatus, false);
return;
}
//二、查看全局事务是否标记为了回滚,如果标记了则回滚,
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;
}
// 三、提交事务
processCommit(defStatus);
}
重点看第三步骤processCommit()
/**
* Process an actual commit.
* Rollback-only flags have already been checked and applied.
* @param status object representing the transaction
* @throws TransactionException in case of commit failure
*/
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
//业务执行完毕之后,如果设置有回滚点,先释放回滚点,业务执行ok,回滚点也没有存在的意义了。
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();
//此处提交事务,跟进doCommit方法内部,会发现调用的是java.sql.Connection对象的commit方法。
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");
}
}
......
// 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 {
//事务处理完毕之后做一些清除或恢复工作
cleanupAfterCompletion(status);
}
}
看下cleanupAfterCompletion()
方法。
/**
* Clean up after completion, clearing synchronization if necessary,
* and invoking doCleanupAfterCompletion.
* @param status object representing the transaction
* @see #doCleanupAfterCompletion
*/
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
//第一步:设置当前事务状态已经完成
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
//第二步:如果是提交事务完毕,则移除当前线程所持有的Connection 连接,即释放连接
doCleanupAfterCompletion(status.getTransaction());
}
//第三步:如果是当前事务中有挂起的资源,则在当前事务执行完毕之后,恢复挂起的资源
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
至此spring 事务的分析也差不多完成了,细节方面的自己看下源码就清楚了。