深入理解@Transactional注解的使用和原理
目录
1.TransactionInterceptor实现拦截
2.TransactionAspectSupport切面
2.1 createTransactionIfNecessary方法
2.1.2 getTransaction()方法
2.1.3 prepareTransactionInfo方法
2.1.4 小节:
2.2 调用目标方法
2.3 completeTransactionAfterThrowing方法
2.4 commitTransactionAfterReturning方法
总结:
@Transactional声明式事务的具体使用方法这里不再重复说明,大家可以参考上一篇文章;
本文主要是带着大家一起看一下@Transactional注解的源码,包括传播机制的实现;
测试案例:下面这两个方法是不同类之间方法调用,并且都加了@Transactional注解的;
@Transactional public void methodA(){ userMapper.selectById("jzcs"); infoService.methodB("userId"); } //methodB是infoService的方法 @Transactional public void methodB(String userId){ infoMapper.selectUserClassInfo(userId); }
我们知道@Transactional是基于aop实现的,那么必定有拦截器和切面;
1.TransactionInterceptor实现拦截
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { Class targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null; Method var10001 = invocation.getMethod(); invocation.getClass(); return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed); }}
TransactionInterceptor会拦截加了@transactional注解的方法,再调用目标方法前,会调用TransactionAspectSupport的invokeWithinTransaction方法对目标方法进行争强;
2.TransactionAspectSupport切面
既然是基于aop,肯定少不了切面对目标方法进行增强了,TransactionAspectSupport.invokeWithinTransaction方法就是对目标前,创建事务和数据库连接,在目标方法执行后,提供事务的提交和回滚,我们看下面源码大家就清楚了:
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { @Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable { //获取@Transactional注解定义的的一些属性(包括隔离级别,传播机制等) TransactionAttributeSource tas = this.getTransactionAttributeSource(); TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null; //创建一个dataSourceTransactionManager,包含数据库信息等 PlatformTransactionManager tm = this.determineTransactionManager(txAttr); //获取目标方法的方法名(就是切点) String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr); Object result; if(){ //省略一部分无关紧要的代码 }else{ //这个很重要,包含有事务和数据库连接等相关信息 TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification); result = null; try { //调用目标方法,访问数据库和业务逻辑处理等 result = invocation.proceedWithInvocation(); } catch (Throwable var17) { //如果调用目标方法报错了,处理回滚 this.completeTransactionAfterThrowing(txInfo, var17); throw var17; } finally { this.cleanupTransactionInfo(txInfo); } //如果调用目标方法没有报错并成功返回,就提交事务 this.commitTransactionAfterReturning(txInfo); return result; }}
只要大家对切面编程aop了解的话,上面的代码就很清晰它的功能了,就是类似于一个环绕通知;
接下来我们就详细了解一下几个具体的方法:
1.createTransactionIfNecessary();创建事务,包含数据库的连接和事务状态等信息;
2.completeTransactionAfterThrowing();调用目标方法报错,回滚;
3.commitTransactionAfterReturning();调用目标方法成功,提交事务(里面有一些判断,不符合条件可能会回滚)
2.1 createTransactionIfNecessary方法
createTransactionIfNecessary()看方法名称就知道,是创建事务,事务包含一些事务本身的信息(传播机制、隔离级别、是否只读、事务的状态、以及数据库连接等)
protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) { //省略部分不重要的代码 TransactionStatus status = null; if (txAttr != null) { if (tm != null) { //创建一个TransactionStatus类型的对象,这个很重要,后面详解 status = tm.getTransaction((TransactionDefinition)txAttr); } else if (this.logger.isDebugEnabled()) { this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured"); } } //封装并返回TransactionAspectSupport.TransactionInfo类型的对象,这个包含了事务的所有信息 return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status); }
如上面代码所示,createTransactionIfNecessary方法创建封装了TransactionAspectSupport.TransactionInfo类型的对象,该对象中包含了事务的所有信息;下面我们就来看里面调用的两个方法:
1.tm.getTransaction(),创建一个TransactionStatus类型的对象,里面包含数据库连接信息和事务状态信息;
2.this.this.prepareTransactionInfo(),封装并返回TransactionAspectSupport.TransactionInfo类型的对象,这个包含了事务的所有信息
2.1.2 getTransaction()方法
调用的是AbstractPlatformTransactionManager类getTransaction方法返回transactionStatus;
该方法做的事情比较多,创建数据库连接,维护TransactionSynchronizationManager的线程变量等
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException { //调用AbstractPlatformTransactionManager的dogetTransaction方法获取事务datasourceTransactionManager.dataSourceTransactionObject类型的txOBject(里面包含有connectionHolder,只不过第一次为空) Object transaction = this.doGetTransaction(); //isExistingTransaction方法用于判定上面的transaction是否从TransactionSynchronizationManager的线程变量resources中获取到connectionHolder,如果获取到了为true,表明是共用同一个事务 if (this.isExistingTransaction(transaction)) { return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled); //判断事物的过期时间 } else if (((TransactionDefinition)definition).getTimeout() < -1) { throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout()); //如果当前事务的传播策略是2,也就是PROPAGATION_MANDATORY,中文翻译为强制,支持使用当前事务,如果当前事务不存在,则抛出Exception。 } else if (((TransactionDefinition)definition).getPropagationBehavior() == 2) { throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"); //如果当前事务的传播策略不是0、默认事务,3、REQUIRES_NEW 创建新事务,6、NESTED:嵌套事务,就走下面的方法 } else if (((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) { if (((TransactionDefinition)definition).getIsolationLevel() != -1 && this.logger.isWarnEnabled()) { this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: " + definition); } boolean newSynchronization = this.getTransactionSynchronization() == 0; return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization, debugEnabled, (Object)null); } else { //我们的传播策略是0,默认策略,走下面的代码 AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = this.suspend((Object)null); if (debugEnabled) { this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition); } try { //newSynchronization为true boolean newSynchronization = this.getTransactionSynchronization() != 2; //newTransactionStatus创建一个transactionStatus对象,表事务的相关状态 DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); //doBegin方法很重要,封装transaction,我们下面详细说明 this.doBegin(transaction, (TransactionDefinition)definition); //prepareSynchronization是维护TransactionSynchronizationManager的线程变量,TransactionSynchronizationManager很重要,我们下面也会详细说明 this.prepareSynchronization(status, (TransactionDefinition)definition); return status; } catch (Error | RuntimeException var7) { this.resume((Object)null, suspendedResources); throw var7; } } }
我们重点讲解doGetTransaction方法、doBegin方法、prepareSynchronization方法:
2.1.2.1 doGetTransaction方法
该方法是尝试从TransactionSynchronizationManager的线程变量resources里面获取connectionHolder即数据库连接,第一个方法肯定是null,但是后续被调用方法共用一个事务的话,就可以从resources里面获取相同的connectionHolder;
protected Object doGetTransaction() { //创建DataSourceTransactionManager的内部类DataSourceTransactionObject对象 DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject(); txObject.setSavepointAllowed(this.isNestedTransactionAllowed()); //尝试从TransactionSynchronizationManager类的线程变量resources里面获取connectionHolder,即获取数据库连接,第一次conHolder肯定为null ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource()); txObject.setConnectionHolder(conHolder, false); return txObject; }
2.1.2.2 doBegin方法
实际调用的DataSourceTransactionManager的doBegin方法,做了下面几件事
1.调用dataSource.getConnection方法获取数据库连接connection
2.将connection封装入ConnectionHolder,并设置synchronizedWithTransaction为true,并将connectionHolder封装到datasourceTransactionManager.dataSourceTransactionObject类型的txOBject对象里面
3.调用dataSourceUtils.prepareConnectionForTransaction方法从注解属性中获取相关值并设置相关属性:
3.1设置数据库连接的事务隔离级别transactionalIsolation,
3.2设置自动提交autoCommit为false,
3.3设置timeout事务过期时间,
3.4设置transactionActive为true
4.将connectionHolder存入TransactionSynchronizationManager的resources,以dataSources为key
下面看源码:
protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction; Connection con = null; try { //第一次肯定没有connectionHolder if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { //第一次肯定需要从数据库连接池中获取数据库连接 Connection newCon = this.obtainDataSource().getConnection(); //将数据库连接封装到ConnectionHolder对象中 txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } //下面的代码就是一些封装了 txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); } //因为是事务,所以不再自动提交 con.setAutoCommit(false); } //该方法无关紧要 this.prepareTransactionalConnection(con, definition); //表示当前数据库连接用于事务 txObject.getConnectionHolder().setTransactionActive(true); int timeout = this.determineTimeout(definition); //设置自定义的事务超时时间 if (timeout != -1) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } //当前的connectionHolder肯定是新的,会走下面的代码 if (txObject.isNewConnectionHolder()) { //该方法将connectionHolder存入TransactionSynchronizationManager的resources,以dataSources为key,后续被调用方法如果共用一个事务,就会从TransactionSynchronizationManager获取到这个connectionHolder,也就是共用一个连接了TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder()); } } catch (Throwable var7) { //省略部分代码 } }
综上,doBegin方法获取了数据库连接封装connectionHoler,和事务绑定,并保存在线程变量TransactionSynchronizationManager.resources中;这个步骤很重要,因为后续访问数据库,就会从线程变量中获取connectionHoler,也就获取数据库连接;被调用方法如果是共用一个事务,也会从该connectionHolder中获取连接,即共用一个事务,就会共用一个数据库连接;
上面提到了TransactionSynchronizationManager类,我们这里看一下这个类有什么?
public abstract class TransactionSynchronizationManager { //存有connectionHolder(当前事务的数据库连接)和sqlSessionHoler(后续访问数据库的会话) private static final ThreadLocal<Map> resources = new NamedThreadLocal("Transactional resources"); //LinkedHashSet 后面访问数据库会存sqlsessionSynchronization对象,属性是sqlsessionFactory和sqlsessionHolder(数据库会话信息) private static final ThreadLocal<Set> synchronizations = new NamedThreadLocal("Transaction synchronizations"); //当前事务的名称,对应@Transaction的transactionManager,没有的话就取方法的全类名 private static final ThreadLocal currentTransactionName = new NamedThreadLocal("Current transaction name"); //当前事务是否是只读事务 false private static final ThreadLocal currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status"); //当前事务的隔离级别 private static final ThreadLocal currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level"); //是否有事务 true private static final ThreadLocal actualTransactionActive = new NamedThreadLocal("Actual transaction active");}
显然这个类里面含有很多线程变量,很多面试官都会问,事务的数据库连接信息是保存在哪里的啊?就是在这个TransactionSynchronizationManager的resources里面;
2.1.2.3 prepareSynchronization方法
如下代码所示,该方法旨在封装TransactionSynchronizationManager的线程变量;具体里面的属性上面已经说明了哈;
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) { //isNewSynchronization为true if (status.isNewSynchronization()) { TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction()); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(definition.getIsolationLevel() != -1 ? definition.getIsolationLevel() : null); TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly()); TransactionSynchronizationManager.setCurrentTransactionName(definition.getName()); TransactionSynchronizationManager.initSynchronization(); } }
综上:整个tm.getTransaction方法,最重要的就是获取到数据库连接,维护了TransactionSynchronizationManager类里面的线程变量,并将数据库连接同事务绑定,封装一个TransactionStatus对象;
2.1.3 prepareTransactionInfo方法
该方法将得到的数据包括TransactionStatus、DataSourceTransactionManager tm、TransactionAttribute txAttr等封装成TransactionAspectSuport.TransactionInfo对象并返回;
protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) { //封装TransactionAspectSupport.TransactionInfo txInfo对象 TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification); if (txAttr != null) { txInfo.newTransactionStatus(status); } else if (this.logger.isTraceEnabled()) { //省略无关紧要的代码 } //该方法的作用暂时不清楚 txInfo.bindToThread(); return txInfo; }
2.1.4 小节:
整个createTransactionIfNecessary()方法旨在:
1.获取数据库连接封装connectionHolder;
2.维护TransactionSynchronizationManager类里面的线程变量,比如将数据库连接信息存入线程变量resources中;
3.封装TransactionStatus对象;
4.封装TransactionAspectSupport.TransactionInfo并返回;
2.2 调用目标方法
Object var8 = invocation.proceedWithInvocation();
这个就是mybatis的内容了,还是有需要提到的地方就是在调用数据库的时候SpringManagedTransaction类的openConnection方法获取数据库连接
private void openConnection() throws SQLException { //获取数据库连接 this.connection = DataSourceUtils.getConnection(this.dataSource); this.autoCommit = this.connection.getAutoCommit(); this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource); if (LOGGER.isDebugEnabled()) { LOGGER.debug("JDBC Connection [" + this.connection + "] will" + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring"); } }
我们具体看一下DataSourceUtils.getConnection方法,里面调用了doGetConnection方法
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource); } catch (SQLException var2) { throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", var2); } catch (IllegalStateException var3) { throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + var3.getMessage()); } }
public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); //从TransactionSynchronizationManager的线程变量resources里面获取connectionHolder,肯定是可以获取到的 ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource); if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) { logger.debug("Fetching JDBC Connection from DataSource"); Connection con = fetchConnection(dataSource); if (TransactionSynchronizationManager.isSynchronizationActive()) { try { ConnectionHolder holderToUse = conHolder; if (conHolder == null) { holderToUse = new ConnectionHolder(con); } else { conHolder.setConnection(con); } holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } catch (RuntimeException var4) { releaseConnection(con, dataSource); throw var4; } } return con; } else { //我们进入下面的逻辑 //获取到connectionHolder后,将connectionHolder的引用数量referenceCount加1,后续访问完数据库,会将conenctionHolder的引用数量减1 conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(fetchConnection(dataSource)); } //返回数据库连接connection return conHolder.getConnection(); } }
看到了没,conHolder.getConnection(),就是从connectionHolder里面获取连接,同一个事务里面,共用connectionHolder,所以同一个事务里面,肯定是共用一个数据库连接的;
2.3 completeTransactionAfterThrowing方法
回到最开始的地方
{ TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification); result = null; try { result = invocation.proceedWithInvocation(); } catch (Throwable var17) { this.completeTransactionAfterThrowing(txInfo, var17); throw var17; } finally { this.cleanupTransactionInfo(txInfo); } this.commitTransactionAfterReturning(txInfo); return result; }
如果调用目标方法失败报错,就会调用completeTransactionAfterThrowing方法;
该方法不是单纯的回滚,里面有个地方有点意思;
我们分析一种场景:
@Transactional public void methodA(){ userMapper.selectById("jzcs"); try { //methodB会报错,但是这里被try catch了,所以methodA就检查不到错误 infoService.getUserClassInfo("userId"); }catch (Exception e){ e.printStackTrace(); } } //methodB是infoService的方法 @Transactional public void methodB(String userId){ infoMapper.selectUserClassInfo(userId); //这个地方肯定会报错 System.out.println(1/0); }
如上所示,在我们的测试案例里面,如果在methodA方法中try catch了methodB方法,如果methodB方法报错了,methodA会不会回滚呢?测试案例里面methodA和methodB是共用的一个事务;
注意:
我们肯定能判定:methodB报错,肯定会走completeTransactionAfterThrowing方法;
methodA没有检查到错误,所以会走commitTransactionAfterReturning方法;
我们带着这个问题去研究代码;
我们看一下methodB走的completeTransactionAfterThrowing方法做了些什么?
protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.getTransactionStatus() != null) { //transactionAttribute.rollbackOn(ex)该方法就是判断是否是抛的运行时异常,一般来说肯定是的 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { //执行回滚,最重还是调用的connection.rollback()方法 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException var6) { this.logger.error("Application exception overridden by rollback exception", ex); var6.initApplicationException(ex); throw var6; } catch (Error | RuntimeException var7) { this.logger.error("Application exception overridden by rollback exception", ex); throw var7; } } else { //省略部分没有用到的代码 } } }
调用AbstractPlatformTransactionManager的rollback方法
public final void rollback(TransactionStatus status) throws TransactionException { //如果当前事务的状态已经是完成的,不允许在回滚 if (status.isCompleted()) { throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"); } else { //调用processRollback方法 DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status; this.processRollback(defStatus, false); } }
进入到AbstractPlatformTransactionManager的processRollback方法
private void processRollback(DefaultTransactionStatus status, boolean unexpected) { try { boolean unexpectedRollback = unexpected; try { this.triggerBeforeCompletion(status); if (status.hasSavepoint()) { if (status.isDebug()) { this.logger.debug("Rolling back transaction to savepoint"); } status.rollbackToHeldSavepoint(); //如果是第一个方法内部执行报错,它的isNewTransaction为true,就会直接执行doRollback回滚 } else if (status.isNewTransaction()) { if (status.isDebug()) { this.logger.debug("Initiating transaction rollback"); } this.doRollback(status); } else { //但是如果是上面说的,是methodB报错,这个是和methodA共用的一个事务,它就会走下面的代码 if (status.hasTransaction()) { //!isGlobalRollbackOnParticipationFailure为false,!status.isLocalRollbackOnly()为true if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {//省略部分代码 } else {//上面说的场景就会走这步if (status.isDebug()) { this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");}//就会执行这个方法,我们详细看一下该代码this.doSetRollbackOnly(status); } } else { this.logger.debug("Should roll back transaction but cannot - no transaction available"); } if (!this.isFailEarlyOnGlobalRollbackOnly()) { unexpectedRollback = false; } } } catch (Error | RuntimeException var8) { this.triggerAfterCompletion(status, 2); throw var8; } this.triggerAfterCompletion(status, 1); if (unexpectedRollback) { throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only"); } } finally { this.cleanupAfterCompletion(status); }
我们详细看一下AbstractPlatformTransactionManager的doSetRollbackOnly这个方法
protected void doSetRollbackOnly(DefaultTransactionStatus status) { DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction(); if (status.isDebug()) { this.logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only"); } //调用setRollbackOnly方法,我们去看一下具体代码做了什么 txObject.setRollbackOnly(); }
调用DataSourceTransactionManager.DataSourceTransactionObject的setRollbackOnly方法
public void setRollbackOnly() { this.getConnectionHolder().setRollbackOnly(); }
看到没有,是调用的connectionHolder的setRollbackOnly()方法,connectionHolder这个类大家还记得吗,就是保存到TransactionSynchronizationManager的线程变量resources里面的;里面包含了数据库连接信息;
我们看一下这个方法干了些什么?
public void setRollbackOnly() { this.rollbackOnly = true; }
再次先提醒:methoA和methodB共用的一个事务,也就共用的一个connectionHolder哦;
设置connectionHolder的rollbackOnly属性为true;这个标识很重要的,因为methodA的内部,try catch 了methodB方法,如果methodB方法报错了,methodB就会在执行completeTransactionAfterThrowing方法的时候,实际是调用了上面分析的方法,将connectionHolder的rollbackOnly属性设置为true;
好methodB就执行完了,接下来我们看看methodA走的commitTransactionAfterReturning方法;
2.4 commitTransactionAfterReturning方法
如果调用目标方法没有报错,就会调用该方法
protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) { if (txInfo != null && txInfo.getTransactionStatus() != null) { if (this.logger.isTraceEnabled()) { this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); } //调用commit方法 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }
进入AbstractPlatformTransactionManager的commit方法
public final void commit(TransactionStatus status) throws TransactionException { //如果事务已经完成,就不允许再次提交,报错 if (status.isCompleted()) { throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"); } else { //我们进入到这里 DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status; //isLocalRollbackOnly为false if (defStatus.isLocalRollbackOnly()) { if (defStatus.isDebug()) { this.logger.debug("Transactional code has requested rollback"); } this.processRollback(defStatus, false); //我们进入到下面方法,!this.shouldCommitOnGlobalRollbackOnly为true,重点是defStatus.isGlobalRollbackOnly()这个方法,我们下面详细看一下这个方法 } else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); } this.processRollback(defStatus, true); } else { this.processCommit(defStatus); } } }
我们进入到isGlobalRollbackOnly方法:
public boolean isGlobalRollbackOnly() { //主要是((SmartTransactionObject)this.transaction).isRollbackOnly()这个判断,我们再进入到这个方法 return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly(); }
再进入到isRollbackOnly()方法:
public boolean isRollbackOnly() { //重点戏来了,获取connectionHolder中的rollbackOnly属性 return this.getConnectionHolder().isRollbackOnly(); }
重点来了:isRollbackOnly()方法,是获取到connectionHolder的rollbackOnly属性,判断是否为true;我们上面的案例里面,methodB报错后,进入到completeTransactionAfterThrowing方法里面,将共用的connectionHolder设置为了true,所以,methodA进入到commitTransactionAfterReturning方法里面发现connectionHolder为true,就会调用processRollback方法执行回滚了;
并且会报错,如下
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
所以,methodA和methodB都会回滚;
当然,如果methodB不报错,肯定就会正常提交啦;
总结:
上面我们介绍了@Transactional注解的源码:
1.TransactionInterceptor负责拦截加了@Transactional注解的方法;
2.TransactionAspectSupport负责对目标方法增强
2.1 在目标方法前创建事务和数据库连接;
2.2 在目标方法报错后执行回滚或者加回滚标识(设置connectionHolder的rollbackOnly为true)
2.3 在目标方法执行成功后执行提交或者回滚(如果检查到connectionHolder的rollbackOnly为true,就会执行回滚,不然就执行提交)