AoP应用之事务(3) 代理类的运行

494 阅读3分钟

调用代理类方法时,如果满足切面表达式,会给方法增加事务拦截器链TransactionInterceptor,之后执行该拦截器的invoke()方法。

论述

TransactionInterceptor->invoke():
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
TransactionAspectSupport->invokeWithinTransaction():
    // 创建事务,向数据库服务器发送 SET autocommit=0
    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); // --1
    Object retVal = null;
    try {
        // 调用下一个拦截器,这里没有,所以是服务类的方法
        retVal = invocation.proceedWithInvocation();
    }
    catch (Throwable ex) {
        // 回滚或提交事务
        // 之后向数据库服务器发送 SET autocommit=1
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    }
    finally {
        cleanupTransactionInfo(txInfo);
    }
    // 提交事务,向数据库服务器发送 COMMIT和 SET autocommit=1
    commitTransactionAfterReturning(txInfo);
    return retVal;

如果invocation.proceedWithInvocation()出错的话,进入以下逻辑。

TransactionAspectSupport->completeTransactionAfterThrowing():
    if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
        try {
            txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
        }
        ......
    }
    else {
        try {
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
        ......
    }

--1处,在新建事务的时候,决定了数据源与连接对象。

// 新建事务,最终会进入以下方法
DataSourceTransactionManager->doBegin():
    // 获得当前数据源的连接对象
    Connection newCon = obtainDataSource().getConnection();
    txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
    ......
    // 当前数据源与当前连接对象绑定
    TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());

单事务下的总结

未命名文件 (7).jpg

定义异常:@Transactional注解中rollbackForrollbackForClassNamenoRollbackFornoRollbackForClassName的属性值
回滚异常:rollbackForrollbackForClassName的属性值,异常符合则回滚
非回滚异常:noRollbackFornoRollbackForClassName的属性值,异常符合则不回滚
运行时异常和系统异常:RuntimeExceptionError异常的子类

根据代码以及流程图可知,存在以下两种情况时,事务不会回滚:

  1. 服务类实例内部调用事务方法,因为这样不会触发AoP代理
  2. 事务方法的异常未被事务拦截器捕捉到,即在事务方法中try catch而未抛给上层

附录:事务的传播机制

事务的传播:以事务执行A方法的过程中,需要执行B方法,此时B方法也需要以事务的方式运行,那么在创建事务时就会发生事务的传播,与propagation属性值有关,事务创建部分的实现位于AbstractPlatformTransactionManager->getTransaction()方法中

事务传播行为类型说明
PROPAGATION_REQUIRED默认的事务传播行为,如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式运行
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED以非事务方式运行操作,如果当前存在事务,就把当前事务挂起
PROPAGATION_NEVER以非事务方式运行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED如果当前没有事务,新建一个事务。如果当前存在事务,则在该事务内作为子事务执行。可以实现嵌套事务回滚而外部事务不回滚,原理是savepoint,如果连接器不支持嵌套事务,会报错

总结:

  • 一个线程最多存在一个活动的事务,如果新建事务,会将当前事务挂起
  • 事务的个数小于等于事务的方法数,即使事务共用,一个事务方法也会完整地执行一次TransactionAspectSupport->invokeWithinTransaction()方法

举例:以下两个事务方法共用一个事务

@Transactional
Demo->test():
    student.test();
    
@Transactional  
Student->test():
    dosomething();