Spring事务执行过程是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
先说一下启动过程中的几个点:
加载配置文件:
AbstractAutowireCapableBeanFactory.doCreateBean –> initializeBean –> applyBeanPostProcessorsAfterInitialization –> beanProcessor.postProcessAfterInitialization –> AbstractAutoProxyCreator.postProcessAfterInitialization –> wrapIfNecessary(bean, beanName, cacheKey) –> getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)Advisor是Pointcut和Advice的配置器,它包括Pointcut和Advice,是将Advice注入程序中Pointcut位置的代码;AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary:调用txAdvice上图的事务通知设置数据源DataSourceTransactionManager,ChooseDataSource是项目中配置的自定义的继承至AbstractRoutingDataSource的默认数据源,命名什么的:
启动结束后,发起事务调用,首先拦截方法起CglibAopProxy.intercept –> ReflectiveMethodInvocation.proceed –> ExposeInvocationInterceptor.invoke –> TransactionInterceptor.invoke:
publicObjectinvoke(finalMethodInvocationinvocation)throwsThrowable{//Workoutthetargetclass:maybe{@codenull}.//TheTransactionAttributeSourceshouldbepassedthetargetclass//aswellasthemethod,whichmaybefromaninterface.ClasstargetClass=(invocation.getThis()!=null?AopUtils.getTargetClass(invocation.getThis()):null);//AdapttoTransactionAspectSupport\'sinvokeWithinTransaction...returninvokeWithinTransaction(invocation.getMethod(),targetClass,newInvocationCallback(){@OverridepublicObjectproceedWithInvocation()throwsThrowable{returninvocation.proceed();}});}
TransactionAspectSupport.invokeWithinTransaction :
determineTransactionManager方法先判断当前事务有无配置特定事务管理器,如果没有判断是否设置过默认的事务管理器,都没有的情况下:
publicPlatformTransactionManagergetTransactionManager(){returnthis.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);}
接下来判断加载的事务属性是否存在或者当前事务管理器是否不是CallbackPreferringPlatformTransactionManager,符合条件会执行到createTransactionIfNecessary,先是:
if(txAttr!=null&&txAttr.getName()==null){txAttr=newDelegatingTransactionAttribute(txAttr){@OverridepublicStringgetName(){returnjoinpointIdentification;}};}
DelegatingTransactionAttribute本身除了被实现后使用没有其他作用。然后从事务管理器中取出事务,doGetTransaction从之前set的数据源取出连接set给DataSourceTransactionObject:
protectedObjectdoGetTransaction(){DataSourceTransactionObjecttxObject=newDataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());ConnectionHolderconHolder=(ConnectionHolder)TransactionSynchronizationManager.getResource(this.dataSource);txObject.setConnectionHolder(conHolder,false);returntxObject;}
然后判断事务是否已存在,如果存在例如嵌套事务,会根据定义的传播方式进行处理,具体处理后面会说,这里是还不存在。然后验证了一下事务是否超时。由于从事务定义(TransactionDefinition持有隔离级别等事务属性的对象)中取出的事务传播方式我这里是默认的PROPAGATION_REQUIRED,第一句用于挂起事务的什么也没做,然后或许事务同步为激活同步,接着就到启动事务了DataSourceTransactionManagerdoBegin:
if(txObject.getConnectionHolder()==null||txObject.getConnectionHolder().isSynchronizedWithTransaction()){ConnectionnewCon=this.dataSource.getConnection();if(logger.isDebugEnabled()){logger.debug(\"AcquiredConnection[\"+newCon+\"]forJDBCtransaction\");}txObject.setConnectionHolder(newConnectionHolder(newCon),true);}
先从数据源中getConnection,set给DataSourceTransactionObject,设置为同步事务,prepareConnectionForTransaction根据配置确定事务是否只读,被嵌套的事务中前一个的隔离级别txObject.setPreviousIsolationLevel(previousIsolationLevel):
判断是否已设置为自动提交,如果是设置则设置事务对象为自动还原为自动提交,有点拗口,意思大概是当事务复用数据库连接时第一个事务提交后,同一个连接的下一个事务还是设置为自动提交,否则当前事务如果被设为手痛提交,因为连接池中的连接会被复用,在同一个连接上的后续事务可能需要手动调用conn.commit才能提交下一个事务,设置connection holder代表的连接的事务是活动的;检查超时;判断当前事务的连接是否是新创建的,是则注册给TransactionSynchronizationManager,通过ThreadLocal将线程和事务绑定;prepareSynchronization设置这个事务同步管理器是否包含实际执行的事务,当前线程事务隔离级别、是否只读以及事务定义名,初始化同步事务(private static final ThreadLocal synchronizations = new NamedThreadLocal(\”Transaction synchronizations\”)):
publicstaticvoidinitSynchronization()throwsIllegalStateException{if(isSynchronizationActive()){thrownewIllegalStateException(\"Cannotactivatetransactionsynchronization-alreadyactive\");}logger.trace(\"Initializingtransactionsynchronization\");synchronizations.set(newLinkedHashSet());}
回到TransactionAspectSupport,prepareTransactionInfo方法创建事务信息,并通过ThreadLocal绑定给当前线程:
TransactionInfotxInfo=newTransactionInfo(tm,txAttr,joinpointIdentification);
privatevoidbindToThread(){//ExposecurrentTransactionStatus,preservinganyexistingTransactionStatus//forrestorationafterthistransactioniscomplete.this.oldTransactionInfo=transactionInfoHolder.get();transactionInfoHolder.set(this);}
绑定中保留活动:慈云数据爆款香港服务器,CTG+CN2高速带宽、快速稳定、平均延迟10+ms 速度快,免备案,每月仅需19元!! 点击查看了当前事务状态,应该用来在嵌套事务中内层事务完成后恢复外层事务的现场。
createTransactionIfNecessary结束,回到TransactionAspectSupport的invokeWithinTransaction方法,接下来是invocation.proceedWithInvocation,这个方法上面贴了,newInvocationCallback时实现了这个方法,代码只有一句调用了MethodInvocation的proceed,也就是环绕通知调用被切方法的方法(也有可能调用其他Interceptor的切面方法),我这直接调用了被切的方法,然而并没有直接走到后面的completeTransactionAfterThrowing或commitTransactionAfterReturning,也没有到清理事务信息的方法,因为有嵌套事务,于是被嵌套的切了,与上面过程相同之处就不说了,说说不同的。
getTransaction这次因为已经存在事务,使用同一个连接,JdbcTransactionObjectSupport实例保存了connectionHolder,所以这次走进了这个判断分支:
if(isExistingTransaction(transaction)){//Existingtransactionfound->checkpropagationbehaviortofindouthowtobehave.returnhandleExistingTransaction(definition,transaction,debugEnabled);}
handleExistingTransaction方法内根据不同的事务传播方式走不同的代码分支,我这就是默认REQUIRED使用现有事务,所以这个方法基本和没走也差不多。嵌套的都完了就走到cleanupTransactionInfo方法了,这方法实际调用了TransactionInfo.restoreThreadLocalStatus,实际上还原了之前的事务信息:
privatevoidrestoreThreadLocalStatus(){//UsestacktorestoreoldtransactionTransactionInfo.//Willbenullifnonewasset.transactionInfoHolder.set(this.oldTransactionInfo);}
然后是commitTransactionAfterReturning:
/***Executeaftersuccessfulcompletionofcall,butnotafteranexceptionwashandled.*Donothingifwedidn\'tcreateatransaction.*@paramtxInfoinformationaboutthecurrenttransaction*/protectedvoidcommitTransactionAfterReturning(TransactionInfotxInfo){if(txInfo!=null&&txInfo.hasTransaction()){if(logger.isTraceEnabled()){logger.trace(\"Completingtransactionfor[\"+txInfo.getJoinpointIdentification()+\"]\");}txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}
commit方法在AbstractPlatformTransactionManager中,先提交的是内层的事务,这里需要提的,嵌套事务的子事务报错但没有抛给外层事务,可能会出现rollback-only的问题,defStatus.isLocalRollbackOnly()就是判断是否有内层事务出错设置rollbackOnly为true了,另外,关于全局事务,似乎说的是用的两段式XA?,不过目前用不上,只是一个连接对数据库,这里可以考虑下。该到具体处理提交的方法processCommit了。同样在AbstractPlatformTransactionManager中。prepareForCommit方法是空的,protected应该是准备给子类重写的,或者这就是我要找的。savepoint没有,由于是内层事务,triggerBeforeCommit、triggerBeforeCompletion、triggerAfterCommit和triggerAfterCommit方法没有执行,设置事务状态后,这个内层事务就提交完了。
ExposeInvocationInterceptor(可以暴露出拦截器链,一般用不到它,用到时应该在链首)还原外层被拦截方法的执行:
@OverridepublicObjectinvoke(MethodInvocationmi)throwsThrowable{MethodInvocationoldInvocation=invocation.get();invocation.set(mi);try{returnmi.proceed();}finally{invocation.set(oldInvocation);}}
这个外层就是实际被拦截的方法,会通过CglibAopProxy执行。
再来就是提交外层事务了,cleanupTransactionInfo的old这次是null了,一样的流程就不说了,由于外层事务是创建了同步对象所以triggerBeforeCommit执行了:
TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
publicstaticvoidtriggerBeforeCommit(booleanreadOnly){for(TransactionSynchronizationsynchronization:TransactionSynchronizationManager.getSynchronizations()){synchronization.beforeCommit(readOnly);}}
这里的beforeCommit由于我用的是mybatis,所以如果执行的话,会执行到SqlSessionUtils.beforeCommit,然而由于SqlSessionUtils是不能被继承的,所以这里不太好动手脚,只能在这个类的外层想办法。triggerBeforeCompletion是类似的。外层事务会有获取status.isGlobalRollbackOnly()用于doCommit(status)之后是否报错,注意,就是说其实并不会打断提交的执行。doCommit(status):
@OverrideprotectedvoiddoCommit(DefaultTransactionStatusstatus){DataSourceTransactionObjecttxObject=(DataSourceTransactionObject)status.getTransaction();Connectioncon=txObject.getConnectionHolder().getConnection();if(status.isDebug()){logger.debug(\"CommittingJDBCtransactiononConnection[\"+con+\"]\");}try{con.commit();}catch(SQLExceptionex){thrownewTransactionSystemException(\"CouldnotcommitJDBCtransaction\",ex);}}
没错,我们用的是druid,这里不需要解释了。之后就是一些完成回调,各种解绑、clear、reset了,之前设置的必须还原为自动提交会在doCleanupAfterCompletion还原,最后关闭连接。
关于Spring事务执行过程是怎样的问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注服务器测评网行业资讯频道了解更多相关知识。
还没有评论,来说两句吧...