Spring中@Transactional实现事务管理的原理

         在日常的web开发中,我们经常会用到数据库的事务,spring中也提供了相应的方法来进行事务的管理。其中,Spring的注解@Transactional是我们常用的管理数据库事务的方法,下面介绍一下@Transactional的原理及使用。

@Transactional实现事务管理的原理

Spring在创建bean对象时,对于带有 @Transactional注解的类,会通过AOP创建一个增强的对象(即BeanPostProcessor 的 postProcessAfterInitialization 方法),比如ServiceA中含有注解@Transactional,spring的IOC流程中,会先根据ServiceA的构造函数创建对象serviceA,然后在初始化时,会调用postProcessAfterInitialization方法创建serviceA的代理对象serviceAProxy,最后Spring的bean工厂存放的对象是serviceA的代理serviceAProxy,这个ServiceAPlus会对原先的方法进行增强, 这样当其它类在创建对象,如果属性中含有ServiceA,在添加属性的时候,加入的属性是serviceAProxy而不是serviceA,如

class ServiceA{

@Transactional

int funcA(){

.......

}

int funcB(){

funcA();

}

}

Class ServiceB{

@autowired

ServiceA serviceA;

}

这个时候创建的对象serviceB中包含的ServiceA属性是serviceAProxy。当serviceB调用ServiceA中的方法funcA时,不是直接调用,而是调用serviceAProxy中的invoke方法(以JDK为例),在invokde方法中,会添加一些数据库相关的操作,如:
                 //看是否有必要创建一个事务,根据事务传播行为,做出相应的判断
               TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
              Object retVal = null;
             try {
             //回调方法执行,执行目标方法(原有的业务逻辑,即ServcieA.funcA())
                         retVal = invocation.proceedWithInvocation();
                }
              catch (Throwable ex) {
                       // 异常回滚
                      completeTransactionAfterThrowing(txInfo, ex);
                      throw ex;
             }
              finally {
      //清除信息
                     cleanupTransactionInfo(txInfo);
             }
    //提交事务
             commitTransactionAfterReturning(txInfo);
            return retVal;

 

这样也就解释了为什么自调用的问题,即在ServiceA中,方法funcB调用funcA,注解@Transactional会失效,因为这个时候是直接调用,而不是通过invoke方法调用代理对象,所以无法对原先的方法增强。

Spring中的事务的传播行为有以下几种:

//事务传播行为类型:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
int PROPAGATION_REQUIRED = 0;
//事务传播行为类型:支持当前事务,如果当前没有事务,就以非事务方式执行。
int PROPAGATION_SUPPORTS = 1;
//事务传播行为类型:当前如果有事务,Spring就会使用该事务;否则会抛出异常
int PROPAGATION_MANDATORY = 2;
//事务传播行为类型:新建事务,如果当前存在事务,把当前事务挂起。
int PROPAGATION_REQUIRES_NEW = 3;
//事务传播行为类型:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
int PROPAGATION_NOT_SUPPORTED = 4;
//事务传播行为类型:即使当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则抛出异常
int PROPAGATION_NEVER = 5;
//事务传播行为类型:如果当前存在事务,则在嵌套事务内执行。
int PROPAGATION_NESTED = 6;

这些传播行为中,有些行为会需要开启一个新的事务(如PROPAGATION_NESTED),这个时候就涉及到了线程上下文的操作,流程如下:

      //挂起上层事务
      SuspendedResourcesHolder suspendedResources = suspend(transaction);

     try {
          //开启本层新事务
          boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);//也是true
         //创建txStatus对象 isNewTransaction是true
        DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        //执行conn.setAutoCommit(false) 以及ConnectionHolder状态设置,以及ConnectionHolder线程绑定
       doBegin(transaction, definition);
       //TransactionSynchronizationManager属性填充
      prepareSynchronization(status, definition);
       return status;
       }
       catch (RuntimeException beginEx) {
               //如果出异常 就恢复上层事务
               resumeAfterBeginException(transaction, suspendedResources, beginEx);
            throw beginEx;
        }
      catch (Error beginErr) {
         resumeAfterBeginException(transaction, suspendedResources, beginErr);
       throw beginErr;
       }

如上,对于需要开启新事务的函数,需要将上层的事务挂起,将外层事务的相关变量(如数据库连接、事务状态等)保存下了,然后清除掉。本层事务重新连接数据库、开始新的事务。当本层事务处理完成后,再将之前保存的相关变量恢复。这样就实现了开启新事务的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值