Starting from a joke

问:把大象放冰箱里,分几步?

答:三步啊,第一、把冰箱门打开,第二、把大象放进去,第三、把冰箱门带上。

问:实现Spring事务,分几步?

答:三步啊,第一、找出需要事务的方法,第二、把事务加进去,第三、执行事务。

You may find it's not a joke, it's serious。

Try to find an entrance

当你面对一个完全不熟悉的事物时,一定要想办法找到一个突破口,然后逐步深入。那Spring事物的突破口在哪里呢?很明显在@EnableTransactionManagement注解里,因为是它启用了事物功能。

请看下图:

发现注解还引入了一个类TransactionManagementConfigurationSelector。

再来看这个类,如下图:

发现如果采用代理的方式时,又引入了一个类ProxyTransactionManagementConfiguration。

接着看这个类(重点来了),如下图:

发现这个类往容器中注册了3个bean,第一个是BeanFactoryTransactionAttributeSourceAdvisor。它以Advisor结尾说明它是Spring AOP范畴里的东西。

在AOP里,Advisor = Pointcut + Advice,Pointcut是切入点,表示要拦截的方法,Advice是增强,表示要加进去的事物功能。

再看看另外两个注册的bean,就是和这两个相关的。其中TransactionInterceptor就是一个Advice,因为它实现了Advice接口,包含了把事物加进去的逻辑。

TransactionAttributeSource虽然不是一个Pointcut,但是它被Pointcut所用,用于检测一个类的方法上是否有@Transactional注解,来确定该方法是否需要事物增强。

从下图中也可以看出这一点:

可以看到这个bean通过下面的set方法被设置进去,然后又用在了Pointcut的类里了。

整体来看,此部分的结构和功能划分还是非常清晰的。下面来逐一研究。

AOP切点

TransactionAttributeSourcePointcut类以Pointcut结尾,说明它是一个切入点,就是标识要被拦截的方法。类名的前缀部分表明了这个切入点的实现原理。

看下这个前缀是TransactionAttributeSource,它以Source结尾,说明它是一个源(即源泉,有向外提供东西的意思)。它的前缀是TransactionAttribute,即事务属性。

由此可见,这个源可以向外提供事务属性,其实就是判断一个类的方法上是否标有@Transactional注解,如果有的话还可以获取这个注解的属性(即事务属性)。

整体来说就是,Pointcut拦截住了方法,然后使用这个“源”去方法和类上获取事务属性,如果能获取到,说明此方法需要参与事务,则进行事务增强,反之则不增强。

下面这张图可以证明我们的想法:

可以看出matches方法的两个参数就是一个方法(Method)和一个类(Class<?>)。最后从方法和类上获取事务属性,再进行是否为null判断。

现在这个“源”还是个黑盒子,下面来揭开它的面纱。它的实现类是AnnotationTransactionAttributeSource,以Annotation开头,说明是基于注解实现的。

下面图是它的源码的一部分:

第一个方法从类上找事务属性,第二个方法从方法上找事务属性,它俩都调用了第三个方法来实现。

PS:我们都知道,方法上的注解优先级高于类上的,是因为找注解时先找方法上的,找不到时再去类上找。所以方法上的优先级高。此部分代码逻辑在父类里写着呢,这里不再展示了。

第三个方法使用多个事务注解解析器(TransactionAnnotationParser)去解析注解,为啥是多个解析器呢?因为事务注解不仅Spring提供了,Java后来也提供了,就是javax.transaction.Transactional。

Spring对自己注解的解析器实现类是SpringTransactionAnnotationParser,如下图:

可以看出使用工具类来读取注解@Transactional的属性,然后逐个解析出属性值并进行类型转换,接着把这些属性封装到一个类里,这个类其实就是事务属性,即TransactionAttribute。

这个事务属性继承了事务定义接口,事务定义接口我们应该都很熟悉,如下图:

这也证明了以前文章里说过的话,@Transactional注解的作用有两个,一是表明要参与事务,二是表明如何参与事务,这些注解属性就是来规定如何参与的。

这个事务属性TransactionAttribute是个接口,它的实现类在这里就不再详说了。

AOP增强

Advice就是AOP中的增强,TransactionInterceptor实现了Advice接口,所以它就是事务增强。

先来看下该接口,如下图:

发现它只是一个空的标记接口。而且它的包名是org.aopalliance,是一个AOP联盟组织,它制定的AOP规范。

先来了解下AOP领域的一些相关内容,Pointcut是切入点,表示要拦截的方法。它是一个静态的概念,即程序不运行时它也是存在的。

那么在真正运行时,已经拦截住了,此时该怎么表示这个情况呢?是用Joinpoint来表示的,所以Joinpoint是一个运行时的概念,只有在运行时才存在。

请看Joinpoint接口,如下图:

第一个方法proceed()是“继续”的意思,调用它表示去执行被拦截住的方法本身,返回方法本身的返回值。

第二个方法getThis()是获取this对象,即方法运行时所在的目标对象。如果是静态方法,则为null,因为静态方法是属于类本身的,运行时不需要对象。

第三个方法getStaticPart(),其实就表示了被拦截住的方法,即就是一个Method。Method其实算是“元数据”,是属于类型本身的,也有“静态”的意思。

再看一个接口,Invocation,它继承了Joinpoint,如下图:

方法getArguments()就表示运行时传递给被拦截住方法的参数。

再看一个接口,MethodInvocation,它继承了Invocation,如下图:

方法getMethod()返回一个Method,它就是当前正在执行的方法,是对本拦截方法的一个友好实现,返回相同的结果。

可见MethodInvocation接口已经包含了一个方法调用的全量信息,方法,参数,目标对象。这其实就是运行时被拦截住的东西。

再看下面这个接口,MethodInterceptor,方法拦截器,如下图:

它只有一个方法invoke,方法参数就是上面介绍的MethodInvocation。所以拦截器可以使用这个参数来对目标方法进行调用,当然在调用前/后可以加入自己的逻辑。

TransactionInterceptor类就实现了这个接口,因此可以在对目标方法的调用前后插入事务逻辑代码来进行事务增强。

下面是事务拦截器对该方法的实现,如下图:

它调用的invokeWithinTransaction方法是在父类里的,看下图:

这个图里做的事情较多,逐个来看:

前两行获取事务属性“源”,再用这个“源”来获取事务属性。咦,有点奇怪,上面不是已经获取过了吗?是的,上面是在Pointcut里获取的,那只是用于判断那个方法是否要被拦截而已。这里获取的属性才是真正用于事务的。

第三行是根据事务属性,来确定出一个事务管理器来。

接下来是使用事务管理器打开事务。

接下来是对被拦截住的目标方法的调用执行,当然要try/catch住这个执行。

如果抛出了异常,则进行和异常相关的事务处理,然后将这个异常继续向上抛出。

如果没有抛出异常,则进行事务提交。

最后的else分支是对编程式事务的调用,事务的打开/提交/回滚是开发人员自己写代码控制,所以就不需要事务管理器操心了。

下面请看和异常相关的事务处理,如下图:

判断异常类型是否需要回滚,需要的话就回滚事务,不需要的话就继续提交事务。

这里的整体结构和逻辑流程也是比较清晰的,那是因为一方面得益于AOP领域的概念,另一方面是事务管理器屏蔽了事务的所有复杂性。

PS:事务管理器的内容其实还是挺复杂的,下篇文章再详细解说。

(END)

编程新说,本号由工作10年

架构师维护,洞察技术本质,

生动幽默有趣,欢迎关注!

【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)的更多相关文章

  1. Spring事务源码阅读笔记

    1. 背景 本文主要介绍Spring声明式事务的实现原理及源码.对一些工作中的案例与事务源码中的参数进行总结. 2. 基本概念 2.1 基本名词解释 名词 概念 PlatformTransaction ...

  2. spring事务源码研读1

    转载摘录自:Spring事务源码分析(一)Spring事务入门 有时为了保证一些操作要么都成功,要么都失败,这就需要事务来保证. 传统的jdbc事务如下: @Test public void test ...

  3. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  4. 结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!

    在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection.但是没有细致的讲到如何绑定,以及为什么这么 ...

  5. 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)

    一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...

  6. spring 事务源码赏析(二)

    我们在spring 事务源码赏析(一) 中分析了spring事务是如何找到目标方法,并如何将事务的逻辑织入到我们的业务逻辑中.本篇我们将会看到spring事务的核心实现: 1.事务传播机制的实现 2. ...

  7. [心得体会]spring事务源码分析

    spring事务源码分析 1. 事务的初始化注册(从 @EnableTransactionManagement 开始) @Import(TransactionManagementConfigurati ...

  8. 【面试】足够“忽悠”面试官的『Spring事务管理器』源码阅读梳理(建议珍藏)

    PS:文章内容涉及源码,请耐心阅读. 理论实践,相辅相成 伟大领袖毛主席告诉我们实践出真知.这是无比正确的.但是也会很辛苦. 就像淘金一样,从大量沙子中淘出金子一定是一个无比艰辛的过程.但如果真能淘出 ...

  9. spring事务源码分析结合mybatis源码(一)

    最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...

随机推荐

  1. 虚拟机console基础环境配置——安装VMware Tools

    1. 虚拟机设置中点击安装2. 虚拟机中挂载VMware Tools镜像3. 解压安装4. 配置共享目录5. 有关VMware Tools 1. 虚拟机设置中点击安装 VMware workstati ...

  2. java 泛型的类型擦除与桥方法

    泛型类 --代码参考:java核心技术 卷1 第十版 public class Pair<T> { private T first; private T second; //构造器 pub ...

  3. iscc2018(一只猫的心思)

    由于这一个杂项类没有更新,所以今天特地来写一下博文.希望能够帮助到你们!!!! 其他关于杂项类的解析,可以查看(https://blog.csdn.net/qq_41187256/article/de ...

  4. nginx用户认证与htpasswd命令

    最近在搭建ELK,然后ELK的kibana界面想添加一个访问限制,看到kibana有个插件x-pack,本来想用用,发现是收费的,就放弃了,然后就想着想配置下nginx的认证访问来实现简单的访问登陆. ...

  5. Django-ORM增删改查

    ORM对单表进行增删改查 一,增加记录 #第一种方式 b=Book(name="Linux",price=66,author="kelvin",pub_date ...

  6. Nginx 配置 Https 免费证书访问

    配置HTTPS 现在做博客或者做网站没有 https 已经不行了,就记录一下我在腾讯云配置 https 的过程吧,非常简单,1个小时就可以了. 还涉及到 http 访问自动转发到 https 访问路径 ...

  7. python中的shutil模块

    目录 python中的shutil模块 目录和文件操作 归档操作 python中的shutil模块 shutil模块对文件和文件集合提供了许多高级操作,特别是提供了支持文件复制和删除的函数. 目录和文 ...

  8. 『线段树 Segment Tree』

    更新了基础部分 更新了\(lazytag\)标记的讲解 线段树 Segment Tree 今天来讲一下经典的线段树. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间 ...

  9. 解决Google Play审核中的WebViewClient.onReceivedSslError问题

    Google Play应用市场提交应用审核,出现因WebViewClient.onReceivedSslError问题导致拒绝通过. Google Paly给出的详情地址:support.google ...

  10. ArrayList 和 LinkedList 源码分析

    List 表示的就是线性表,是具有相同特性的数据元素的有限序列.它主要有两种存储结构,顺序存储和链式存储,分别对应着 ArrayList 和 LinkedList 的实现,接下来以 jdk7 代码为例 ...