深入理解@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)) {

            //封装事物,设置该事物不是新的事物,注意,这里的封装对象和第一次进来的事物对象不是同一个对象,只是共用了一个connectionHolder对象罢了,这里会设置该事物对象的newTransaction=false,为后面判断回滚时候需要;
            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<Object, Object>> resources = new NamedThreadLocal("Transactional resources");

    //LinkedHashSet  后面访问数据库会存sqlsessionSynchronization对象,属性是sqlsessionFactory和sqlsessionHolder(数据库会话信息)
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");

    //当前事务的名称,对应@Transaction的transactionManager,没有的话就取方法的全类名
    private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");

    //当前事务是否是只读事务   false
    private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");

    //当前事务的隔离级别
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
    
    //是否有事务 true
    private static final ThreadLocal<Boolean> 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,就会执行回滚,不然就执行提交)

 
 

  • 17
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wait疯man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值