Spring事务原理浅析

·  阅读 803

1:springBoot开启事务

只要在启动类上加上该注解,就可以表示启用事务了
@EnableTransactionManagement
@SpringBootApplication
public class NettyChatApplication {

    public static void main(String[] args) {
        SpringApplication.run(NettyChatApplication.class, args);
    }

}

//只要在需要使用事务的方法上加上  注解即可
复制代码

既然开启事务和使用事务是二个注解,那么只能从这二个注解开始入手了。先看下 @Transactional 注解 ,发现什么都没有,只有一些注解该有的标志信息

image.png

所以现在只能靠 @EnableTransactionManagement 注解了,该注解中有一个 @Import注解,这个注解的作用就是将该注解中的Bean注入到Spring容器中,也就是这里的 TransactionManagementConfigurationSelector.class, 所以我们进入这个类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
复制代码

这里重点关注 ProxyTransactionManagementConfiguration.class,进入这个类

image.png

这个类是一个配置类,里面注入了很多Bean,包括这里的一个拦截器 ,这个拦截器的作用就是当执行的方法上有 @Transactional 注解,就会经过该拦截器拦截并进行处理,你可以在该拦截器中的 invoke() 方法上打上一个断点,然后执行一个加 @Transactional 注解的方法,程序就会停在断点上,如下图

image.png

image.png

所以我们要分析Spring事务的原理的入口就是在该拦截器的 invoke() 方法

2:事务原理分析入口

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
   Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

   // 事务的入口方法
   return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
复制代码

我们进入该方法,这个方法我只把一些核心方法截取出来了

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {

   //获取@Trancation注解中的一些信息,比如该注解配置的超时时间,事务隔离级别等一些信息
   TransactionAttributeSource tas = getTransactionAttributeSource();
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
   final TransactionManager tm = determineTransactionManager(txAttr);

   PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
   final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

   if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
      // 开始创建事务
      TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

      Object retVal;
      try {
         //执行自己的业务逻辑
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // 有异常就回滚
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }

      
      // 事务提交
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
}
复制代码

3:开始创建事务

开始创建事务是在这一行代码,进入该方法

TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
复制代码
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
      @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

   // 这里设置事务的一些名字之类的信息,无关的代码
   if (txAttr != null && txAttr.getName() == null) {
      txAttr = new DelegatingTransactionAttribute(txAttr) {
         @Override
         public String getName() {
            return joinpointIdentification;
         }
      };
   }

   TransactionStatus status = null;
   if (txAttr != null) {
      if (tm != null) {
         // 核心方法:去获取一个事务
         status = tm.getTransaction(txAttr);
      }
      else {
         if (logger.isDebugEnabled()) {
            logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                  "] because no transaction manager has been configured");
         }
      }
   }
   //组装该事务的一些信息,然后返回
   return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
复制代码

我们只需要去分析获取事务的方法就行

AbstractPlatformTransactionManager.class

@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
      throws TransactionException {

   // 尝试去获取一个事务,第一次进来肯定是空的,这里就涉及到事务的传播机制了
   // 这种情况一般是在一个类中,一个方法调用了另外一个方法,二个方法都有@Transaction注解,
   // 此时执行到另外一个方法的时候,判断该方法上的事务隔离级别,根据不同的事务隔离级别来判断
   // 是继续使用该事务还是创建一个新的事务
   // 现在是第一次进来,所以这里肯定是为空的
   Object transaction = doGetTransaction();
   boolean debugEnabled = logger.isDebugEnabled();

   if (isExistingTransaction(transaction)) {
      // 第一次进来的时候是没有事务的,所以不会走到这里来
      return handleExistingTransaction(def, transaction, debugEnabled);
   }

   // 如果设置的超时时间小于-1,直接抛出异常
   if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
      throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
   }

   // 如果事务的传播机制是: PROPAGATION_MANDATORY抛出异常
   if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
      throw new IllegalTransactionStateException(
            "No existing transaction found for transaction marked with propagation 'mandatory'");
   }
   
   //如果是以下几种传播机制就开启一个事务
   else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
         def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
         def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
      SuspendedResourcesHolder suspendedResources = suspend(null);
      
      try {
         // 核心方法:开启一个事务
         return startTransaction(def, transaction, debugEnabled, suspendedResources);
      }
      catch (RuntimeException | Error ex) {
         resume(null, suspendedResources);
         throw ex;
      }
   }
}
复制代码

默认的传播机制是 Propagation.REQUIRED,所以会进入 startTransaction(def, transaction, debugEnabled, suspendedResources); 这个方法

image.png

private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
      boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

   boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
   DefaultTransactionStatus status = newTransactionStatus(
         definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
   // 核心方法:开启事务
   doBegin(transaction, definition);
   prepareSynchronization(status, definition);
   return status;
}
复制代码
protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
    Connection con = null;

    try {
       
        //获取一个数据库的连接
        con = txObject.getConnectionHolder().getConnection();
       
        // 设置是否只读
        txObject.setReadOnly(definition.isReadOnly());
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            // 设置自动提交为false
            con.setAutoCommit(false);
        }

        this.prepareTransactionalConnection(con, definition);
        txObject.getConnectionHolder().setTransactionActive(true);
        int timeout = this.determineTimeout(definition);
        if (timeout != -1) {
            txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
        }

        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
        }

    }
}
复制代码

总结:假设你现在项目整合的是MyBatis,然后该方法操作了不同的数据表,MyBaitis首先会获取一个SqlSession,也就是一个数据库连接,这里操作二次数据库,所以会获取二次的数据库连接。而Spring的事务是依赖MySql事务的。

MySql中,我们开启一个事务是 beging -> 执行SQL -> commit/rollback。整个事务内都是在一个数据库连接中完成的,所以Spring要让事务有效,所有的数据库操作都必须在同一个数据库连接中完成。所以在开启一个事务这里,需要单独获取一个数据库连接,然后,后续的数据库操作都是用该数据库连接了,MyBatis不会再重新去获取一个数据库连接去操作数据了

// 更新用户信息
userMapper.updateById(user);
//更新班级信息
classMapper.updateById(1);
复制代码

所以我们就可以知道在开启事务就是获取一个新的数据库连接,设置自动提交为false,然后设置一些超时时间等等,该连接用于后续该事务中所有的数据库操作

4: 执行自己的业务逻辑

retVal = invocation.proceedWithInvocation();
复制代码

这里就是执行我们的业务逻辑了,如果有关于数据库的操作,都是使用上一步获取的数据路连接去操作数据了,而不是重新去获取一个新的数据库连接

5:事务提交

commitTransactionAfterReturning(txInfo);
复制代码

我们先看事务提交的逻辑

protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
   if (txInfo != null && txInfo.getTransactionStatus() != null) {
      if (logger.isTraceEnabled()) {
         logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
      }
      //提交事务
      txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
   }
}
复制代码
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");
   }

   DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
   if (defStatus.isLocalRollbackOnly()) {
      if (defStatus.isDebug()) {
         logger.debug("Transactional code has requested rollback");
      }
      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);
}
复制代码
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
   try {
      boolean beforeCompletionInvoked = false;

      try {
         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(status);
         }
       
      }
}
复制代码
protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
    
    //通过事务上下文拿到开启事务创建的那个数据库连接
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        // 执行提交
        con.commit();
    } catch (SQLException var5) {
        throw new TransactionSystemException("Could not commit JDBC transaction", var5);
    }
}
复制代码

事务提交:就是通过事务上下文拿到开启事务那一步创建的数据库连接,然后执行commit操作,就相当于我们在MySql客户端执行的 commit 是一样的。

5:事务回滚

completeTransactionAfterThrowing(txInfo, ex);
复制代码
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
   if (txInfo != null && txInfo.getTransactionStatus() != null) {
      
      // txInfo.transactionAttribute.rollbackOn(ex)这里会判断异常类型,如果不匹配就不会回滚,就会走到下面的else分支,去执行提交的操作
      // 事务无效的一个原因之一:就是捕获的异常不匹配
      if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
         try {
            // 执行事务回滚操作
            txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
         }
      }
      else {
         // 异常不匹配就会执行事务提交操作
         try {
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
         }
      }
   }
}
复制代码

进入回滚操作

@Override
public final void rollback(TransactionStatus status) throws TransactionException {
  
   DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
   //事务回滚
   processRollback(defStatus, false);
}
复制代码
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
   try {
      boolean unexpectedRollback = unexpected;

      try {
         triggerBeforeCompletion(status);

         if (status.hasSavepoint()) {
            if (status.isDebug()) {
               logger.debug("Rolling back transaction to savepoint");
            }
            status.rollbackToHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
            if (status.isDebug()) {
               logger.debug("Initiating transaction rollback");
            }
            // 事务回滚
            doRollback(status);
         }    
}
复制代码
protected void doRollback(DefaultTransactionStatus status) {
    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
    //通过事务上下文拿到开启事务创建的那个数据库连接
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        // 执行回滚
        con.rollback();
    } catch (SQLException var5) {
        throw new TransactionSystemException("Could not roll back JDBC transaction", var5);
    }
}
复制代码

事务回滚:就是通过事务上下文拿到开启事务那一步创建的数据库连接,然后执行rollback操作,就相当于我们在MySql客户端执行的 rollback 是一样的。

6事务流程总结

  • 1:开启事务:获取一个新的数据库连接,然后设置该连接的自动提交为false,然后设置一些别的信息,比如超时时间等等

  • 2:执行自己业务逻辑:拿到上一步获取到的数据库连接,去执行数据库的操作

  • 3:如果没有异常,就通过事务的上下文拿到第一步获取的数据库连接,然后执行提交操作

  • 4:如果有异常,首先判断异常类型是否匹配,如果匹配则通过事务上下文拿到第一步获取的数据库连接,然后执行回滚操作,如果不匹配就执行事务提交

在这里只是事务的一个最简单的流程,还有很多点是没有分析到的,比如在开启事务那里,如果已经存在事务了,就会进入另外一个处理逻辑,当然还有事务的挂起,事务的恢复等等,但是只要把整体的流程弄清楚了,这些分支就可以慢慢的去研究了

@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
      throws TransactionException {

   // 尝试去获取一个事务,第一次进来肯定是空的,这里就涉及到事务的传播机制了
   // 这种情况一般是在一个类中,一个方法调用了另外一个方法,二个方法都有@Transaction注解,
   // 此时执行到另外一个方法的时候,判断该方法上的事务隔离级别,根据不同的事务隔离级别来判断
   // 是继续使用该事务还是创建一个新的事务
   // 现在是第一次进来,所以这里肯定是为空的
   Object transaction = doGetTransaction();
   boolean debugEnabled = logger.isDebugEnabled();

   if (isExistingTransaction(transaction)) {
      // 第一次进来的时候是没有事务的,所以不会走到这里来
      return handleExistingTransaction(def, transaction, debugEnabled);
   }

复制代码
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改