61 张图,剖析 Spring 事务,就是要钻到底!

网站建设3年前发布
96 0 0

大家好,我是楼仔!,下面我会简单介绍一下 Spring 事务的基础知识,以及使用方法,然后直接对源码进行拆解。,不 BB,上文章目录。,202303061353421827d9765f93e07077b467a51f7d5cf894dc43341,需要搭建环境的同学,代码详见:https://github.com/lml200701158/program_demo/tree/main/spring-transaction,下面是 DB 数据和 DB 操作接口:,基础测试代码,testSuccess() 是事务生效的情况:,执行入口:,输出:,为了方便大家能更好看懂后面的源码,我先整体介绍一下源码的执行流程,让大家有一个整体的认识,否则容易被绕进去。,整个 Spring 事务源码,其实分为 2 块,我们会结合上面的示例,给大家进行讲解。20230306135235790a02828e60246f3bd44776accb9832484193999,第一块是后置处理,我们在创建 Louzai Bean 的后置处理器中,里面会做两件事情:,获取 Louzai 的切面方法:首先会拿到所有的切面信息,和 Louzai 的所有方法进行匹配,然后找到 Louzai 所有需要进行事务处理的方法,匹配成功的方法,还需要将事务属性保存到缓存 attributeCache 中。,创建 AOP 代理对象:结合 Louzai 需要进行 AOP 的方法,选择 Cglib 或 JDK,创建 AOP 代理对象。,2023030613523566619c70139ebdf4a391515a467130516cc11d253,第二块是事务执行,整个逻辑比较复杂,我只选取 4 块最核心的逻辑,分别为从缓存拿到事务属性、创建并开启事务、执行业务逻辑、提交或者回滚事务。,注意:Spring 的版本是 5.2.15.RELEASE,否则和我的代码不一样!!!,上面的知识都不难,下面才是我们的重头戏,让你跟着楼仔,走一遍代码流程。,2023030613523504fc60d414b2eae440e4597f82b17d91d4a66e120,20230306135237583c46f36fc0fd9f1841071b74c87cdb6710b2368,这里需要多跑几次,把前面的 beanName 跳过去,只看 louzai。,202303061352376389d7303476e292097070d782456fdf3b308a403,20230306135343640f07c9629b16f0ea3679f0fc857475edd7de920,进入 doGetBean(),进入创建 Bean 的逻辑。,20230306135238667b11f07e72da4f0c4487b77583093098d139943,进入 createBean(),调用 doCreateBean()。,20230306135238d9c638803043551d9f6347ff143774e4ed5f5d893,进入 doCreateBean(),调用 initializeBean()。,20230306135239e95073d928c1fb0603490381dfd882a8e3a66d205,20230306135343f7deafe37194c5d06ec179fa00083b9ff327a6351,2023030613524026a084394a60d1920b446714587d24123617f4245,2023030613524144af06235420b1e158d2759575f5b6082bd4ff847,如果看过我前面几期系列源码的同学,对这个入口应该会非常熟悉,其实就是用来创建代理对象。,2023030613524178b522457d1a399ce38790060f661d4d040cd2219,这里是重点!敲黑板!!!,先获取 louzai 类的所有切面列表;,创建一个 AOP 的代理对象。,20230306135242e97a50e2691dae86eed7761b42876bfa70d7aa737,3.2.1 获取切面列表,2023030613524212f41f322c7c31c242e686365d33176c6b8a5c549,这里有 2 个重要的方法,先执行 findCandidateAdvisors(),待会我们还会再返回 findEligibleAdvisors()。,20230306135343879efff90542a416e06699a30fab296504eae5490,2023030613524385bccf498c4575c6ae166994701e9ec5160e01800,20230306135244c148b9d51b7b6d1c8d88735aba15d99108c232362,2023030613524563204a723852d116c31516dc5d26f8fc73bf4f857,依次返回,重新来到 findEligibleAdvisors()。,20230306135245e8d5df2753a902daad26973eb0f36ae64c56f7449,2023030613524521b0a9017aab48eeb868294f9eaa712a622149724,20230306135346b5f8ab442c68d80378570436d4c06040267f87769,20230306135347057ad2344c9f487e2694867ab599fb1d6c6b93132,进入 canApply(),开始匹配 louzai 的切面。,20230306135247371206689a90ac3cc199066f831399ca356263865,这里是重点!敲黑板!!!,这里只会匹配到 Louzai.testSuccess() 方法,我们直接进入匹配逻辑。,20230306135247423c7fa659cb75461972493ea1a22d8ebfbd04692,如果匹配成功,还会把事务的属性配置信息放入 attributeCache 缓存。,20230306135248f3e12cb560d9c460e6b54982e90e1134a802a3250,2023030613524933ee26d40b8a86da916774bc09f680be008c82205,20230306135249242090020a088c0454477883853dd9e210facb700,20230306135348a3980eb1790ad8e1e096216662154ce3838bcc741,2023030613534979982fa71853e7b3320448ce1bdd7a89942c74231,20230306135250a9d0063740d2cdad9c6869b4e02f81bde64950989,我们依次返回到 getTransactionAttribute(),再看看放入缓存中的数据。,2023030613525136d36cd23bd60afed0644988d49a047b6eda58571,再回到该小节开头,我们拿到 louzai 的切面信息,去创建 AOP 代理对象。,20230306135252a524658754e11f608766039575db288c4d2fe7234,3.2.2 创建 AOP 代理对象,创建 AOP 代理对象的逻辑,在上一篇文章(Spring AOP)讲解过,我是通过 Cglib 创建,感兴趣的同学可以关注公众号「楼仔」,翻一下楼仔的历史文章。,回到业务逻辑,通过 louzai 的 AOP 代理对象,开始执行主方法。,20230306135252d9a508290dfb5b9798a8915a4c9ad30afdf688353,因为代理对象是 Cglib 方式创建,所以通过 Cglib 来执行。,20230306135252f90756777f095f66d496152c3eec6c9c4a14ac478,2023030613534837fa46d9382175756f5921609c40e140ec6386905,20230306135253338127a683a8922b95d8447482d4c079aab113614,20230306135254a1778dd010c4b874f266232fb84058a6a57890310,这里是重点!敲黑板!!!,下面的代码是事务执行的核心逻辑 invokeWithinTransaction()。,20230306135255b321f0e5166c5115b49309344bf99d94b700d3546,3.3.1 获取事务属性,在 invokeWithinTransaction() 中,我们找到获取事务属性的入口。,20230306135255e8868b5361dc84898e99649857898222546eaf941,从 attributeCache 获取事务的缓存数据,缓存数据是在 “2.2.1 获取切面列表” 中保存的。,20230306135255620906350314bcb5858662303eb14ad91448d3234,3.3.2 创建事务,20230306135351091ab2533a43624af47988c799e0ad3df12d08862,202303061352570963b0459d1f94ad89b090c57e2737c6d2e920691,20230306135257a8ec62800e05f96d8d8008ad3d37dcdd697292898,通过 doGetTransaction() 获取事务。,通过 startTransaction() 开启事务。,2023030613525747ced1229ede57475fc4513f3c22b12c35c7fd153,下面是开启事务的详细逻辑,了解一下即可。,最后返回到 invokeWithinTransaction(),得到 txInfo 对象。,2023030613525873899da613e21e60ac943652fe4e429271d6d7226,3.3.3 执行逻辑,还是在 invokeWithinTransaction() 中,开始执行业务逻辑。,20230306135258b891285809a4f8765bd680456713c60deb9eef593,20230306135353c7b9790880c697ce2c902153dee031ebe48ec2115,20230306135259b32ba8a1485f13088607823dba348924d4a854670,20230306135300a8dccc6104e2188964e673a21f6c73b02596c5710,202303061353013479d326461b389e08004066f9cab56d509a34551,进入到真正的业务逻辑。,202303061353012330b01468b285aa7543470090f6dd1f72d5da736,执行完毕后抛出异常,依次返回,走后续的回滚事务逻辑。,3.3.4 回滚事务,还是在 invokeWithinTransaction() 中,进入回滚事务的逻辑。,2023030613530179fa77237b781c4a174758822d5591077dca81446,。,20230306135353a93ef3f912e92e54f05748a2eccd84fab4c9d2533,执行回滚逻辑很简单,我们只看如何判断是否回滚。,20230306135304958e8d205f121c33f2f8745584c0775639c344324,20230306135304985798e16afe8574c292104b2035e89f53eddb721,2023030613530484dd77f04d34956983f05927452e804f5a2ece537,如果抛出的异常类型,和事务定义的异常类型匹配,证明该异常需要捕获。,之所以用递归,不仅需要判断抛出异常的本身,还需要判断它继承的父类异常,满足任意一个即可捕获。,20230306135304e5baa2e9566295def7b069f8589f99e7492e6c741,到这里,所有的流程结束。,我们再小节一下,文章先介绍了事务的使用示例,以及事务的执行流程。,之后再剖析了事务的源码,分为 2 块:,先匹配出 louzai 对象所有关于事务的切面列表,并将匹配成功的事务属性保存到缓存;,从缓存取出事务属性,然后创建、启动事务,执行业务逻辑,最后提交或者回滚事务。

© 版权声明

相关文章