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. BZOJ_3626_[LNOI2014]LCA_离线+树剖

    BZOJ_3626_[LNOI2014]LCA_离线+树剖 题意: 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度, ...

  2. k8s编排最佳实践

    编排文件技巧 使用资源时指定最新稳定版的API version 编排文件应该纳入版本控制,这样可以在必要的时候快速回滚,同样也有利于资源恢复和重建 使用YAML格式而不是JSON格式,尽管两种格式的文 ...

  3. java中的数组二分法

    数组二分法意在以较快的速度查找到某个值的下标位置. 二分法的核心思想:找到一个数组的中间位置值,判断某个数值是在这个中间值的左边还是右边,如果是左边,将中间位置之前进行二分,二分后,结束位置变为原始中 ...

  4. SQL Server 锁详解

    锁是一种防止在某对象执行动作的一个进程与已在该对象上执行的其他进行相冲突的机制.也就是说,如果有其他人在操作某个对象,那么你旧不能在该对象上进行操作.你能否执行操作取决于其他用户正在进行的操作. 通过 ...

  5. RecyclerView 刷新后自动滚动的问题,notifyDataSetChanged 后自己滚动

    把recyclerview 高度设为match_parent就解决了..... source: https://segmentfault.com/q/1010000005966966

  6. 小程序 表单 获取 formId

    微信小程序使用模板消息需要使用支付prepay_id或表单提交formId, 要获得 formId 需要在 form 标签中声明属性    report-submit="true" ...

  7. POLARDB · 最佳实践 · POLARDB不得不知道的秘密

    ## 前言 POLARDB作为阿里云下一代关系型云数据库,自去年9月份公测以来,收到了不少客户的重点关注,今年5月份商业化后,许多大客户开始陆续迁移业务到POLARDB上,但是由于POLARDB的很多 ...

  8. 搭建基于Docker社区版的Kubernetes本地集群

    Kubernetes的本地集群搭建是一件颇费苦心的活,网上有各种参考资源,由于版本和容器的不断发展,搭建的方式也是各不相同,这里基于Docker CE的18.09.0版本,在Mac OS.Win10下 ...

  9. Nuget(BagGet)使用教程

    Nuget(BagGet)使用教程 1. 服务器安装ASP.NET Core 网上有很多教程,不多讲,链接给你:https://www.cnblogs.com/Agui520/p/8331499.ht ...

  10. js防抖和节流

    今天在网上看到的,里面的内容非常多.说下我自己的理解. 所谓的防抖就是利用延时器来使你的最后一次操作执行.而节流是利用时间差的办法,每一段时间执行一次.下面是我的代码: 这段代码是右侧的小滑块跟随页面 ...