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

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领域的概念,另一方面是事务管理器屏蔽了事务的所有复杂性。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容