spring源码阅读--@Transactional实现原理讲解

目录
  • @Transactional注解简介
  • spring中声明式事务实现原理猜想
  • @Transactional作用
  • 动态代理逻辑实现
  • TransactionInterceptor–最终事务管理者
  • 总结

@Transactional注解简介

@Transactional是spring中声明式事务管理的注解配置方式,相信这个注解的作用大家都很清楚。

@Transactional注解可以帮助我们把事务开启、提交或者回滚的操作,通过aop的方式进行管理。

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

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

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

  • 首先,对于spring中aop实现原理有了解的话,应该知道想要对一个方法进行代理的话,肯定需要定义切点。在@Transactional的实现中,同样如此,spring为我们定义了以 @Transactional 注解为植入点的切点,这样才能知道@Transactional注解标注的方法需要被代理。
  • 有了切面定义之后,在spring的bean的初始化过程中,就需要对实例化的bean进行代理,并且生成代理对象。
  • 生成代理对象的代理逻辑中,进行方法调用时,需要先获取切面逻辑,@Transactional注解的切面逻辑类似于@Around,在spring中是实现一种类似代理逻辑。

@Transactional作用

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

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

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

其中【aop实现原理分析】知道在AopUtils#findAdvisorsThatCanApply中判断切面是否适用当前bean,可以在这个地方断点分析调用堆栈,AopUtils#findAdvisorsThatCanApply一致调用,最终通过以下代码判断是否适用切点。

AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<?> targetClass) 

这里可以根据参数打上条件断点进行调试分析调用栈,targetClass就是目标class

…一系列调用 最终

SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
    //这里就是分析Method是否被@Transactional注解标注,有的话,不用说BeanFactoryTransactionAttributeSourceAdvisor适配当前bean,进行代理,并且注入切点
    //BeanFactoryTransactionAttributeSourceAdvisor
   AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
   if (attributes != null) {
      return parseTransactionAnnotation(attributes);
   }
   else {
      return null;
   }
}

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

动态代理逻辑实现

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

DynamicAdvisedInterceptor#intercept 

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

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Class<?> targetClass = null;
   Object target = null;
   try {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // May be null. Get as late as possible to minimize the time we
      // "own" the target, in case it comes from a pool...
      target = getTarget();
      if (target != null) {
         targetClass = target.getClass();
      }
       //follow
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
         // We need to create a method invocation...
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null) {
         releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

通过分析

List<Object> 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 
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
	// Work out the target class: may be {@code null}.
	// The TransactionAttributeSource should be passed the target class
	// as well as the method, which may be from an interface.
	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
	// Adapt to TransactionAspectSupport's invokeWithinTransaction...
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
		@Override
		public Object proceedWithInvocation() throws Throwable {
			return invocation.proceed();
		}
	});
}

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

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
      throws Throwable {
   // If the transaction attribute is null, the method is non-transactional.
   final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass);
   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      // Standard transaction demarcation with getTransaction and commit/rollback calls.
       //开启事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal = null;
      try {
         // This is an around advice: Invoke the next interceptor in the chain.
         // This will normally result in a target object being invoked.
          //方法调用
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // target invocation exception
   		//回滚事务
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
       //提交事务
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
   else {
      // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      try {
         Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
               new TransactionCallback<Object>() {
                  @Override
                  public Object doInTransaction(TransactionStatus status) {
                     TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                     try {
                        return invocation.proceedWithInvocation();
                     }
                     catch (Throwable ex) {
                        if (txAttr.rollbackOn(ex)) {
                           // A RuntimeException: will lead to a rollback.
                           if (ex instanceof RuntimeException) {
                              throw (RuntimeException) ex;
                           }
                           else {
                              throw new ThrowableHolderException(ex);
                           }
                        }
                        else {
                           // A normal return value: will lead to a commit.
                           return new ThrowableHolder(ex);
                        }
                     }
                     finally {
                        cleanupTransactionInfo(txInfo);
                     }
                  }
               });
         // Check result: It might indicate a Throwable to rethrow.
         if (result instanceof ThrowableHolder) {
            throw ((ThrowableHolder) result).getThrowable();
         }
         else {
            return result;
         }
      }
      catch (ThrowableHolderException ex) {
         throw ex.getCause();
      }
   }
}

总结

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

分析源码后对照

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring事务的开启原理详解

    在事务配置类上声明@EnableTransactionManagement注解开启事务 在事务配置类上定义数据源 在事务配置类上定义事务管理器 在相关类或者方法上使用@Transactional声明事务 代码如下: @Configuration @EnableTransactionManagement public class RootConfig{ @Bean public DataSource dataSource(){ DruidDataSource dataSource = new Dr

  • Spring 事务事件监控及实现原理解析

    前面我们讲到了Spring在进行事务逻辑织入的时候,无论是事务开始,提交或者回滚,都会触发相应的事务事件.本文首先会使用实例进行讲解Spring事务事件是如何使用的,然后会讲解这种使用方式的实现原理. 1. 示例 对于事务事件,Spring提供了一个注解@TransactionEventListener,将这个注解标注在某个方法上,那么就将这个方法声明为了一个事务事件处理器,而具体的事件类型则是由TransactionalEventListener.phase属性进行定义的.如下是Transac

  • 从Spring源码解析事务失效的原因

    目录 一.前言 二.方法不是 public 的 三.内部方法间调用导致事务失效 四.异常类型是否配置正确 五.异常被catch住 一.前言 1.Bean是否是代理对象 2.入口函数是否是public的 3.数据库是否支持事务(Mysql的Mvlsam不支持事务),行锁才支持事务 4.切点是否配置正确 5.内部方法间调用导致事务失效 因为this不是代理对象,可以配置 expose-proxy="true",就可以通过AopContext.currentProxy()获取到当前类的代理对

  • 浅谈Spring中@Transactional事务回滚及示例(附源码)

    一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除某个部门的时候,假设我们默认删除对应的成员.但是在执行的时候可能会出现这种情况,我们先删除部门,再删除成员,但是部门删除成功了,删除成员的时候出异常了.这时候我们希望如果成员删除失败了,之前删除的部门也取消删除.这种场景就可以使用@Transactional事物回滚. 二.checked异常和unc

  • spring源码阅读--@Transactional实现原理讲解

    目录 @Transactional注解简介 spring中声明式事务实现原理猜想 @Transactional作用 动态代理逻辑实现 TransactionInterceptor–最终事务管理者 总结 @Transactional注解简介 @Transactional是spring中声明式事务管理的注解配置方式,相信这个注解的作用大家都很清楚. @Transactional注解可以帮助我们把事务开启.提交或者回滚的操作,通过aop的方式进行管理. 通过@Transactional注解就能让spr

  • spring源码阅读--aop实现原理讲解

    目录 aop实现原理简介 代理实现的处理器(BeanPostProcessor) 代理实现的源头–AnnotationAwareAspectJAutoProxyCreator AnnotationAwareAspectJAutoProxyCreator的继承结构 代理对象(Proxy)的创建 解析并缓存切面 适配切面 aop实现原理简介 首先我们都知道aop的基本原理就是动态代理思想,在设计模式之代理模式中有介绍过这两种动态代理的使用与基本原理,再次不再叙述. 这里分析的是,在spring中是如

  • 教你使用IDEA搭建spring源码阅读环境的详细步骤

    目录 第一步.准备gradle环境 第二步.下载spring源码 第一步.准备gradle环境 1.去官网下载gradle https://gradle.org/releases/ 2.将其解压缩,创建repository文件夹 和init.d文件夹 创建init.gradle文件 输入文本信息,主要是配置阿里云镜像仓库地址,和maven的类似 gradle.projectsLoaded { rootProject.allprojects { buildscript { repositories

  • 基于Gradle搭建Spring 5.3.13-release源码阅读环境的详细流程

    目录 # 1.安装JDK # 2.安装Gradle # 3.Spring版本命名规则 # 4.下载Spring 5.3.13-release源码 # 5.修改Spring源码中Gradle配置 # 6.构建Spring源码 # 7.导入IDEA 点击File --> New --> Project from Existing Sources... # 8.创建Spring源码debug调试模块 # 基于Gradle搭建Spring 5.5.13-release源码阅读环境 Spring版本:5

  • grade构建阅读spring源码环境 Idea2020.3的过程

    笔者看了很多博客,从早上搞到晚上,终于成功了

  • springboot-2.3.x最新版源码阅读环境搭建(基于gradle构建)

    一.前言 跟很多小伙伴聊天,发现一个严重的问题,很多小伙伴横向发展的貌似很不错,很多技术都能说出一二,但是如果在某个技术上深挖一下就不行了,问啥啥不会.就拿springboot来说,很多同学止步于springboot的应用,再往深处就一问三不知了,那么如何破局呢?smart哥认为最好的办法就是直捣黄龙,要把一个技术理解透了,听别人讲一万遍原理,不如自己撕一遍源码. 要阅读源码那就首先得先搭建源码阅读环境,那么本篇文章就来介绍下Spring Boot的源码环境搭建. 鉴于spring团队已经全面抛

  • Spring源码解析之编程式事务

    一.前言 在Spring中,事务有两种实现方式: 编程式事务管理: 编程式事务管理使用TransactionTemplate可实现更细粒度的事务控制.声明式事务管理: 基于Spring AOP实现.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务. 声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单(尤其是配合spring boot自动配置,可以说是精简至极!),且大部分业务都可

  • Three.js源码阅读笔记(物体是如何组织的)

    这是Three.js源码阅读笔记第三篇.之前两篇主要是关于核心对象的,这些核心对象主要围绕着矢量vector3对象和矩阵matrix4对象展开的,关注的是空间中的单个顶点的位置和变化.这一篇将主要讨论Three.js中的物体是如何组织的:即如何将顶点.表面.材质组合成为一个具体的对象. Object::Mesh 该构造函数构造了一个空间中的物体.之所以叫"网格"是因为,实际上具有体积的物体基本都是建模成为"网格"的. 复制代码 代码如下: THREE.Mesh =

  • spring源码学习之bean的初始化以及循环引用

    实例化方法,把bean实例化,并且包装成BeanWrapper 1.点进这个方法里面. 这个方法是反射调用类中的 factoryMethod 方法. 这要知道@Bean 方法的原理, 实际上spring 会扫描有@bean 注解的方法, 然后把方法名称设置到 BeanDefinition 的 factoryMethod属性中, 接下来就会调到上面截图中的方法实现@Bean 方法的调用. 2. 有参构造函数的时候 determineConstructorsFromBeanPostProcessor

  • Idea 搭建Spring源码环境的超详细教程

    本篇主要讲解如何使用Ideal 搭建Spring的源码环境,想必大家都会多多少少去看过Spring的部分源码,一般我们都是直接点进某个Spring类 然后Idea上面去下载 ,但是确实比较麻烦,而且不能添加自己对源码的注释 理解 ,本篇就来解决这个问题,手把手使用Idea 搭建Spring framework ,并且直接在Spring framework项目中添加我们自己的module 来验证环境是否正确. 本过程会比较耗时 而且容易出错 慢慢来吧. 1. clone spring-framew

随机推荐