来源:编程新说


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:事务管理器的内容其实还是挺复杂的,下篇文章再详细解说。

足够应付面试的Spring事务源码阅读梳理相关推荐

  1. 【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  2. 事务注解放到类上面 下面私有方法有效吗_【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)...

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  3. Spring源码分析-Spring事务源码分析

    导语      在配置Spring事务管理的时候会用到一个类TransactionInterceptor,从下面的类关系图中可以看到TransactionInterceptor继承了MethodInt ...

  4. spring事务源码解析

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

  5. Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析

      基于最新Spring 5.x,详细介绍了Spring 事务源码,包括< tx:annotation-driven/>标签源码解析.   此前我们已经学习了Spring的<tx:a ...

  6. Spring事务源码分析责任链事务链事务不生效

    文章目录 前言 带着问题分析源码 事务源码分析 寻找Spring事务源码类 TransactionInterceptor调用栈 分析Spring AOP责任链 分析TransactionInterce ...

  7. Spring事务源码(一)

    Spring事务源码(一) 前言 一.ProxyTransactionManagementConfiguration 二.BeanFactoryTransactionAttributeSourceAd ...

  8. Spring框架源码阅读读后感

    Spring框架源码阅读读后感 spring的bean生命周期,从上到下依次完成,本人在阅读源码时总结得出此步骤,当然,spring是一个强大的框架,其对bean的生命周期管理只是其中的一部分,本人也 ...

  9. Spring事务源码详解

    一. 简介 事务: 事务是逻辑上的一组操作,要么都执行,要么都不执行,关于事务的基本知识可以看我的这篇文章:事务的基础知识 Spring事务: Spring 支持两种方式的事务管理:编程式事务管理.声 ...

最新文章

  1. 华为路由器 android,华为智能路由器亮相 Android系统很强大
  2. php/eq,thinkphp中eq标签的使用
  3. 运行与windows命令(cmd)的说明与示例
  4. 三分钟免费搞定网站在线客服,利用PowerTalkBox控件制作而成,为大家提供比较好的示例...
  5. memcached(五)--源码分析,启动
  6. 预定义类型未定义或导入_探索类型系统的底层 - 自己实现一个 TypeScript
  7. c# 条件编译 Conditional (DEBUG)
  8. 让Thread#stop方法无法终止你的线程
  9. 魔百盒CM201-2-YS代工-刷机固件及教程
  10. linux Ubuntu系统安装百度aip
  11. 蓝宝石(Al2O3)晶体基片
  12. java读取文件服务器文件,java读取远程服务器文件
  13. “我怎么就被一张照片出卖了?”
  14. IO复用模型同步,异步,阻塞,非阻塞及实例详解
  15. 基于MFC和OpenCV的图像处理小软件
  16. WebMatrix进阶教程(1):如何安装和使用微软全新开发工具WebMatrix
  17. java遍历是什么意思_遍历Java String行的最佳方法是什么?
  18. 完全背包问题贪心算法c语言,数据结构与算法学习之路:背包问题的贪心算法和动态规划算法...
  19. 计算机的投资:量化投资
  20. springboot2.x集成ureport2.2.9搭建报表引擎

热门文章

  1. 强如 Disruptor 也发生内存溢出?
  2. 实习小白::(转) Cocos2d-x 3.0开发(五)关联程序逻辑与cocoStudio导出文件
  3. linux Makefile编写的整理
  4. mysqldump 导出某几张表
  5. Matlab中legend位置
  6. socket通过多网卡收发数据
  7. awk print 的用法
  8. 【机器学习入门笔记8:TensorFlow运算实质】20190216
  9. 运算服务器v9型号,v9云服务器
  10. 将特定像素点在图像上连接起来_图像分割【论文解读】快速图像分割的SuperBPD方法 CVPR-2020...