• Spring的@Transactional如何实现的


    一、@Transactional注解简介

    @Transactional是spring中声明式事务管理的注解配置方式,相信这个注解的作用大家都很清楚。@Transactional注解可以帮助我们把事务开启、提交或者回滚的操作,通过aop的方式进行管理。

    通过@Transactional注解就能让spring为我们管理事务,免去了重复的事务管理逻辑,减少对业务代码的侵入,使我们开发人员能够专注于业务层面开发。

    我们知道实现@Transactional原理是基于spring aop,aop又是动态代理模式的实现,通过对源码的阅读,总结出下面的步骤来了解实际中,在spring 是如何利用aop来实现@Transactional的功能的。

    二、spring中声明式事务实现原理猜想

    首先,对于spring中aop实现原理有了解的话,应该知道想要对一个方法进行代理的话,肯定需要定义切点。在@Transactional的实现中,同样如此,spring为我们定义了以 @Transactional 注解为植入点的切点,这样才能知道@Transactional注解标注的方法需要被代理。

    有了切面定义之后,在spring的bean的初始化过程中,就需要对实例化的bean进行代理,并且生成代理对象。

    生成代理对象的代理逻辑中,进行方法调用时,需要先获取切面逻辑,@Transactional注解的切面逻辑类似于@Around,在spring中是实现一种类似代理逻辑。

    三、@Transactional作用

    根据上面的原理猜想,下面简单介绍每个步骤的源码以进行验证。

    首先是@Transactional,作用是定义代理植入点。我们知道代理对象创建的通过BeanPostProcessor的实现类AnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInstantiation方法来实现个,如果需要进行代理,那么在这个方法就会返回一个代理对象给容器,同时判断植入点也是在这个方法中。

    那么下面开始分析,在配置好注解驱动方式的事务管理之后,spring会在ioc容器创建一个BeanFactoryTransactionAttributeSourceAdvisor实例,这个实例可以看作是一个切点,在判断一个bean在初始化过程中是否需要创建代理对象,都需要验证一次BeanFactoryTransactionAttributeSourceAdvisor是否是适用这个bean的切点。如果是,就需要创建代理对象,并且把BeanFactoryTransactionAttributeSourceAdvisor实例注入到代理对象中。

    前文我们知道在AopUtils#findAdvisorsThatCanApply中判断切面是否适用当前bean,可以在这个地方断点分析调用堆栈,AopUtils#findAdvisorsThatCanApply一致调用,最终通过以下代码判断是否适用切点。

    • AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class targetClass) 这里可以根据参数打上条件断点进行调试分析调用栈,targetClass就是目标class …一系列调用

    • 最终SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)

    1. @Override
    2. public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
    3.     //这里就是分析Method是否被@Transactional注解标注,有的话,不用说BeanFactoryTransactionAttributeSourceAdvisor适配当前bean,进行代理,并且注入切点
    4.     //BeanFactoryTransactionAttributeSourceAdvisor
    5.    AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
    6.    if (attributes != null) {
    7.       return parseTransactionAnnotation(attributes);
    8.    }
    9.    else {
    10.       return null;
    11.    }
    12. }

    上面就是判断是否需要根据@Transactional进行代理对象创建的判断过程。@Transactional的作用一个就是标识方法需要被代理,一个就是携带事务管理需要的一些属性信息。

    四、动态代理逻辑实现

    【aop实现原理分析】中知道,aop最终的代理对象的代理方法是

    • DynamicAdvisedInterceptor#intercept

    所以我们可以在这个方法断点分析代理逻辑。

    1. @Override
    2. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    3.    Object oldProxy = null;
    4.    boolean setProxyContext = false;
    5.    Class targetClass = null;
    6.    Object target = null;
    7.    try {
    8.       if (this.advised.exposeProxy) {
    9.          // Make invocation available if necessary.
    10.          oldProxy = AopContext.setCurrentProxy(proxy);
    11.          setProxyContext = true;
    12.       }
    13.       // May be null. Get as late as possible to minimize the time we
    14.       // "own" the target, in case it comes from a pool...
    15.       target = getTarget();
    16.       if (target != null) {
    17.          targetClass = target.getClass();
    18.       }
    19.        //follow
    20.       List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    21.       Object retVal;
    22.       // Check whether we only have one InvokerInterceptor: that is,
    23.       // no real advice, but just reflective invocation of the target.
    24.       if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
    25.          // We can skip creating a MethodInvocation: just invoke the target directly.
    26.          // Note that the final invoker must be an InvokerInterceptor, so we know
    27.          // it does nothing but a reflective operation on the target, and no hot
    28.          // swapping or fancy proxying.
    29.          Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    30.          retVal = methodProxy.invoke(target, argsToUse);
    31.       }
    32.       else {
    33.          // We need to create a method invocation...
    34.          retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    35.       }
    36.       retVal = processReturnType(proxy, target, method, retVal);
    37.       return retVal;
    38.    }
    39.    finally {
    40.       if (target != null) {
    41.          releaseTarget(target);
    42.       }
    43.       if (setProxyContext) {
    44.          // Restore old proxy.
    45.          AopContext.setCurrentProxy(oldProxy);
    46.       }
    47.    }
    48. }
    49. 通过分析 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)返回的是TransactionInterceptor,利用TransactionInterceptor是如何实现代理逻辑调用的?

      跟踪new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

      发现最终是调用TransactionInterceptor#invoke方法,并且把CglibMethodInvocation注入到invoke方法中,从上面可以看到CglibMethodInvocation是包装了目标对象的方法调用的所有必须信息,因此,在TransactionInterceptor#invoke里面也是可以调用目标方法的,并且还可以实现类似@Around的逻辑,在目标方法调用前后继续注入一些其他逻辑,比如事务管理逻辑。

      五、TransactionInterceptor–最终事务管理者

      下面看代码。

      • TransactionInterceptor#invoke

      1. @Override
      2. public Object invoke(final MethodInvocation invocation) throws Throwable {
      3.  // Work out the target class: may be {@code null}.
      4.  // The TransactionAttributeSource should be passed the target class
      5.  // as well as the method, which may be from an interface.
      6.  Class targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
      7.  // Adapt to TransactionAspectSupport's invokeWithinTransaction...
      8.  return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
      9.   @Override
      10.   public Object proceedWithInvocation() throws Throwable {
      11.    return invocation.proceed();
      12.   }
      13.  });
      14. }

      继续跟踪invokeWithinTransaction,下面的代码中其实就可以看出一些逻辑端倪,就是我们猜想的实现方式,事务管理。

      1. protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation)
      2.       throws Throwable {
      3.    // If the transaction attribute is null, the method is non-transactional.
      4.    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
      5.    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
      6.    final String joinpointIdentification = methodIdentification(method, targetClass);
      7.    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      8.       // Standard transaction demarcation with getTransaction and commit/rollback calls.
      9.        //开启事务
      10.       TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      11.       Object retVal = null;
      12.       try {
      13.          // This is an around advice: Invoke the next interceptor in the chain.
      14.          // This will normally result in a target object being invoked.
      15.           //方法调用
      16.          retVal = invocation.proceedWithInvocation();
      17.       }
      18.       catch (Throwable ex) {
      19.          // target invocation exception
      20.      //回滚事务
      21.          completeTransactionAfterThrowing(txInfo, ex);
      22.          throw ex;
      23.       }
      24.       finally {
      25.          cleanupTransactionInfo(txInfo);
      26.       }
      27.        //提交事务
      28.       commitTransactionAfterReturning(txInfo);
      29.       return retVal;
      30.    }
      31.    else {
      32.       // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      33.       try {
      34.          Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
      35.                new TransactionCallback() {
      36.                   @Override
      37.                   public Object doInTransaction(TransactionStatus status) {
      38.                      TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
      39.                      try {
      40.                         return invocation.proceedWithInvocation();
      41.                      }
      42.                      catch (Throwable ex) {
      43.                         if (txAttr.rollbackOn(ex)) {
      44.                            // A RuntimeException: will lead to a rollback.
      45.                            if (ex instanceof RuntimeException) {
      46.                               throw (RuntimeException) ex;
      47.                            }
      48.                            else {
      49.                               throw new ThrowableHolderException(ex);
      50.                            }
      51.                         }
      52.                         else {
      53.                            // A normal return value: will lead to a commit.
      54.                            return new ThrowableHolder(ex);
      55.                         }
      56.                      }
      57.                      finally {
      58.                         cleanupTransactionInfo(txInfo);
      59.                      }
      60.                   }
      61.                });
      62.          // Check result: It might indicate a Throwable to rethrow.
      63.          if (result instanceof ThrowableHolder) {
      64.             throw ((ThrowableHolder) result).getThrowable();
      65.          }
      66.          else {
      67.             return result;
      68.          }
      69.       }
      70.       catch (ThrowableHolderException ex) {
      71.          throw ex.getCause();
      72.       }
      73.    }
      74. }
      75. 六、总结

        最终可以总结一下整个流程,跟开始的猜想对照。

        分析源码后对照

      76. 相关阅读:
        使用vue-pdf预览pdf文件
        SpringMVC异常处理器
        【无标题】
        【freertos】006-任务切换实现细节
        简简单单搞一个实用的Android端搜索框
        多表联查--07--- Hash join
        工作9年,1次转行,4次跳槽:拒绝被「稳定性」绑架
        Linux CentOS使用Docker部署Apache Superset并实现远程分析数据
        牛客小白月赛59
        Visual Studio 2019安装详解
      77. 原文地址:https://blog.csdn.net/qq_37284798/article/details/126161104