最详细的Spring事务机制+手写

353 阅读5分钟

前言

最近看SpringBoot源码的时候突然想起之前的事务机制及其传播,于是认真的去看了下相关源码,特给大家分享,并手动实现一个简单版本的,主要观光流程如下。

基本思想

SpringBoot事务传播的机制主要是将连接和事务管理器绑定起来,以及新事务出现的时候需要将之前的事务挂起,也就是解绑。当REQUIRES_NEW的时候是会创建一个新的ConnectionHolderConnectionHolder就是当前连接Connection的一个拥有者,用来获取当前连接。

源码解析

首先让我们看下拦截器入口

public Object invoke(MethodInvocation invocation) throws Throwable {
	//加载目标类
        Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        //加载目标类的方法
        Method var10001 = invocation.getMethod();
        invocation.getClass();
        //进行AOP对事务的一些处理(核心)
        return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
    }

然后进入invokeWithinTransaction方法,这时候我们不走if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) {这个条件,因为我们的事务管理不是CallbackPreferringPlatformTransactionManager,所以我们进入else

//根据字面意思就是根据情况是否创建新事务,如果创建了返回新事务,否则老的
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

            try {
            	//进行拦截目标方法的调用
                result = invocation.proceedWithInvocation();
            } catch (Throwable var17) {
            	//出现异常之后的一些处理,比如回滚
                this.completeTransactionAfterThrowing(txInfo, var17);
                throw var17;
            } finally {
            	//清除当前事务相关信息,将之前的事务加入
                this.cleanupTransactionInfo(txInfo);
            }
			//提交当前事务
            this.commitTransactionAfterReturning(txInfo);
            return result;

这里我们主要看下createTransactionIfNecessary

if (txAttr != null && ((TransactionAttribute)txAttr).getName() == null) {
  	    //如果当前事务相关信息为空,就给个默认的
            txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) {
                public String getName() {
                    return joinpointIdentification;
                }
            };
        }

        TransactionStatus status = null;
        if (txAttr != null) {
            if (tm != null) {
                //获取事务对象
                status = tm.getTransaction((TransactionDefinition)txAttr);
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
            }
        }

        return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);

让我们进入getTransaction方法一探究竟,因为平时基本就用REQUIREDREQUIRED_NEW,就拿这两个讲啦,其他的类似。

 //判断是否之前就有事务,如果有进行一些处理
 if (this.isExistingTransaction(transaction)) {
            return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
//这里就是对REQUIRES_NEW的相关处理啦
if (definition.getPropagationBehavior() == 3) {
                if (debugEnabled) {
                    this.logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]");
                }
				//挂起当前事务
                suspendedResources = this.suspend(transaction);

                try {
                    newSynchronization = this.getTransactionSynchronization() != 2;
                    //新建一个事务状态
                    DefaultTransactionStatus status = this.newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                    //开始事务(核心)
                    this.doBegin(transaction, definition);
                    //进行事务管理的一些属性设置和同步设置
                    this.prepareSynchronization(status, definition);
                    return status;
                } catch (Error | RuntimeException var7) {
                	//恢复之前挂起的事务
                    this.resumeAfterBeginException(transaction, suspendedResources, var7);
                    throw var7;
                }

让我们看看事务是如何挂起的,进去suspend方法,点击进去再进入doSuspend,可以看到DataSourceTransactionManager

protected Object doSuspend(Object transaction) {
		//得到当前线程的事务对象
        DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
        //将当前对象的连接清除,因为要挂起事务,放下下一个事务	
        txObject.setConnectionHolder((ConnectionHolder)null);
        //之前的事务解绑
        return TransactionSynchronizationManager.unbindResource(this.obtainDataSource());
    }

这时候之前的事务就被挂起啦,接下来就是对新事务,连接的建立和绑定流程啦。

//判断当前线程是否已经有了连接了,因为刚刚对这个是清空了的,就是解绑那里,这里再验证一遍
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
		//这里获取一个新的连接
                Connection newCon = this.obtainDataSource().getConnection();
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
                }
		//将新连接和当前线程的事务对象绑定
                txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
            }

            txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
            //获取连接Holder
            con = txObject.getConnectionHolder().getConnection();
            Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
            //设置当前连接是否可读
            txObject.setPreviousIsolationLevel(previousIsolationLevel);
            //关闭自动提交
            if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
                }

                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());
            }

其实到这里就能看出核心了,为什么REQUIRE_NEW发生回滚的时候不会影响父方法,因为它们是两个不同的连接,总的来说,整体思想就是,如果当前需要创建新事务,也就是REQUIRE_NEW状态,则用新连接去绑定当前的事务管理,也就是我们当前使用哪个连接去操作,这时候子方法是REQUIRE_NEW状态,出现异常我们进行回滚不会影响到父方法,因为我们绑定的是当前子方法的连接。

进行手写一个

连接相关操作工具类

/**
 * @author chujun
 * @version 1.0
 * @date 2021-03-06 22:42
 */
@Component
public class ConnectionHolderUtil {

    private static DataSource dataSource;


    private static final Logger  log =  LoggerFactory.getLogger(ConnectionHolderUtil.class);

    @Autowired
    public  void setDataSource(DataSource dataSource) {
        ConnectionHolderUtil.dataSource = dataSource;
    }

    private static ThreadLocal<ConnectionHolder> connectionHolderThreadLocal = new ThreadLocal<>();

    /**
     * 获取数据库连接
     * @return Connection
     */
    public static ConnectionHolder getConnectionHolder(boolean isNew){
        ConnectionHolder connectionHolder = connectionHolderThreadLocal.get();
        //如果有连接,并不需要生成新的直接返回
        if(connectionHolder != null && !isNew){
            return connectionHolder;
        }
        try {
            //获取新连接
            Connection connection = dataSource.getConnection();

            //关闭自动提交
            connection.setAutoCommit(false);
            connectionHolder = new ConnectionHolder(connection);
            connectionHolderThreadLocal.set(connectionHolder);

            //绑定连接
            TransactionSynchronizationManager.bindResource(dataSource,connectionHolder);
            return connectionHolder;
        } catch (SQLException e) {
            log.error("数据库连接获取失败",e);
            return null;
        }
    }

    /**
     * 提交事务
     */
    public static void commit(){
        ConnectionHolder connectionHolder = connectionHolderThreadLocal.get();
        if(connectionHolder == null){
            return;
        }
        try {
            connectionHolder.getConnection().commit();
        } catch (SQLException e) {
            log.error("提交失败",e);
        }
    }

    /**
     * 事务回滚
     */
    public static void rollback(){
        ConnectionHolder connectionHolder = connectionHolderThreadLocal.get();
        if(connectionHolder == null){
            return;
        }
        try {
            connectionHolder.getConnection().rollback();
        } catch (SQLException e) {
            log.error("回滚失败",e);
        }
    }

    /**
     * 关闭连接
     */
    public static void close(){
        ConnectionHolder connectionHolder = connectionHolderThreadLocal.get();
        if(connectionHolder == null){
            return;
        }
        Connection connection = connectionHolder.getConnection();
        try {
            connection.close();
        } catch (SQLException e) {
            log.error("数据库连接关闭失败",e);
        }
    }

    /**
     * 恢复挂起的事务
     */
    public static void resume(Object susPend){
        TransactionSynchronizationManager.unbindResource(dataSource);
        TransactionSynchronizationManager.bindResource(dataSource,susPend);
        connectionHolderThreadLocal.set((ConnectionHolder) susPend);
    }

    /**
     * 挂起当前事务
     */
    public static Object hangTrasaction(){
        return TransactionSynchronizationManager.unbindResource(dataSource);
    }

    /**
     * 判断当前连接是否已经关闭
     * @return
     */
    public static boolean isClose(){
        if(connectionHolderThreadLocal.get() == null){
            return true;
        }
        try {
            return connectionHolderThreadLocal.get().getConnection().isClosed();
        } catch (SQLException e) {
            log.error("获取连接状态失败");
        }
        return true;
    }

}

AOP切面拦截

/**
 * @author chujun
 * @version 1.0
 * @date 2021-02-28 15:31
 */
@Aspect
@Component
public class TransactionAspect{

    private static final Logger log = LoggerFactory.getLogger(TransactionAspect.class);

    @Pointcut("@annotation(com.cj.annotion.Transactional)")
    public void point(){}

    @Around("point()")
    public Object transactionHandle(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Transactional transactional = methodSignature.getMethod().getAnnotation(Transactional.class);

        //方法执行结果
        Object result = null;
        //被挂起的事务
        Object supSend = null;

        boolean isNew = false;

        //如果事务类型是 require require_new
        if(transactional.propagation() == Propagation.REQUIRED || transactional.propagation() == Propagation.REQUIRES_NEW)
        {
            //如果事务类型是 require
            if(transactional.propagation() == Propagation.REQUIRED){
                //有事务则用原来的事务
                 ConnectionHolderUtil.getConnectionHolder(false);
            }
            //如果事务类型是 require_new
            else {
                //挂起当前事务
                //新建一个连接事务,然后绑定到事务同步管理器
                //绑定新连接
                supSend = ConnectionHolderUtil.hangTrasaction();
                //获取一个新连接
                isNew = true;
                ConnectionHolderUtil.getConnectionHolder(true);
            }
            try {
                //执行拦截的方法
                result = joinPoint.proceed();
                //提交事务
                if(!ConnectionHolderUtil.isClose()) {
                    ConnectionHolderUtil.commit();
                }
            }catch (Exception e){
                //回滚事务
                if(!ConnectionHolderUtil.isClose()) {
                    ConnectionHolderUtil.rollback();
                }
            }finally {
                //关闭连接
                if(isNew) {
                    ConnectionHolderUtil.close();
                }
                 //恢复之前挂起的事务
                if(supSend != null) {
                    ConnectionHolderUtil.resume(supSend);
                }
            }
        }
        //否则
        else {
            log.info("事务类型暂时不支持");
        }
        log.info("切面执行完成");
        return result;
    }
}

事务注解

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {

    Propagation propagation() default Propagation.REQUIRED;

    Class <? extends Throwable>[] rollBackFor() default {};
}

传播方式

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

具体调用客户端

@Service
public class BlogService {

    @Autowired
    private BlogDao blogDao;

    @Transactional(propagation = Propagation.REQUIRED)
    public void testMyTransaction(Blog blog){
        blogDao.insertBlog(blog);
        BlogService blogService  = (BlogService) AopContext.currentProxy();
        try {
          blogService.testMyTransaction2(blog);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void testMyTransaction2(Blog blog){
        blogDao.insertBlog(blog);
        int a = 1/0;
    }
}

如上图,这时候如果testMyTransaction2出现异常,是不会影响到testMyTransaction的,因为它们是不同事务。

总结

Spring事务管理确实也用到,我也是接触这块没多久,但是之前只会用,经过这几天面试,我发现学习东西一定要深入的去了解,只会用可不行。不知道大家有没有发现存ConnectionHolder的时候用的ThreadLocal,这样是为了多个客户端同时请求的时候互不干扰,线程隔离的,如果不清楚ThreadLocal的可以了解一下ThreadLocal使用和原理,面试有被问到。

最后的最后,感谢大家浏览,如有任何错误欢迎指出。