首页>>后端>>Spring->Transaction Management源码阅读路径

Transaction Management源码阅读路径

时间:2023-11-30 本站 点击:1

前言

本文主要记录笔者学习Transaction Management的学习路径,读者可以对比自己的学习路径,一起讨论出探讨出更优学习路径。

基本路径

找到官方文档。

找到Overview页面,了解项目提供的能力、能实现的效果、设计理念、历史、最低要求等等。

找到Quick Start,快速把它的环境搭建起来,对项目有个感性的认知。

找到想了解的模块

找到模块的Introduction,一般这里有项目的基本介绍,你需要找到这个模块用到的基本概念或者名词介绍文档。

使用场景、功能特性以及相关生态系统介绍

了解核心/共用/复用模块

确定学习目标(遇到的问题、心存疑问/好奇),以点带面的阅读方式来阅读源码

提炼升华

结合实际开发/项目,可以解决什么问题、原来处理有什么不足/缺陷

日常思考、联想、完善

接下来将根据基本路径展开,源码时序图只画出核心点,具体还需读者自行调试。

Spring Framework Overview

参考

Quick Start

传送门

想要了解的模块

Transaction Management

使用场景和功能特性

Spring Framework全面支持事务,为事务管理提供了一致性的抽象。跨不同事务API的一致性编程模型。支持声明式事务管理。与Spring的数据库访问抽象的完美集成。

核心

通过阅读前面文章,使用声明式事务的话,可以大概知道Spring Framework的核心有事务策略的抽象-PlatformTransactionManager、管理每个线程的资源和事务同步的中央委托-TransactionSychrnoizationManager、使用AOP代理包装合适的bean的一种BeanPostProcessor-AbstractAutoProxyCreator、配合PlatformTransactionManager实现驱动事务around method的TransactionInterceptor。

下面将通过一个简单的事务示例(使用mysql、mybatis-plus、spring-boot),画一个时序图来看看核心类起到的作用。

示例

importlombok.SneakyThrows;importorg.mybatis.spring.annotation.MapperScan;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.context.annotation.AdviceMode;importorg.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication@EnableTransactionManagement@MapperScan(basePackages={"com.whf.spring.dao"})publicclassApplication{@SneakyThrowspublicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}importcom.whf.spring.service.business.TryService;importorg.junit.jupiter.api.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.test.context.junit4.SpringRunner;importorg.springframework.test.context.web.WebAppConfiguration;@RunWith(SpringRunner.class)@SpringBootTest(classes=Application.class)@WebAppConfigurationclassApplicationTests{@AutowiredprivateTryServicetryService;@TestvoidtestTransaction(){tryService.test();}}

importcom.whf.spring.annotation.SimpleAnnotation;importcom.whf.spring.service.UserService;importcom.whf.spring.service.business.TryService;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.ApplicationEventPublisher;importorg.springframework.context.annotation.Scope;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;@Service@Scope@Slf4jpublicclassTryServiceImplimplementsTryService{@AutowiredprivateUserServiceuserService;@Override@SimpleAnnotation@Transactional(rollbackFor=Exception.class)publicvoidtest(){log.info("执行业务");userService.test();}}

importcom.whf.spring.dao.UserDao;importcom.whf.spring.entity.UserDo;importcom.whf.spring.service.UserService;importorg.springframework.stereotype.Service;importcom.baomidou.mybatisplus.service.impl.ServiceImpl;@Service("userService")publicclassUserServiceImplextendsServiceImpl<UserDao,UserDo>implementsUserService{@Overridepublicvoidtest(){this.selectById(1);thrownewRuntimeException();}}

大致调用如下:

源码时序图

核心的顺序应该是:

AbstractAutoProxyCreator 创建代理

调用事务必经过TransactionInterceptor

TransactionInterceptor会调用PlatformTransactionManger进行事务管理

PlatformTransactionManger会调用TransactionSychrnoizationManager进行事务资源同步

核心类的源码如下,围绕核心的一些其他源码时序图在其他章节会涉及。

创建代理时序图

可以看到,代理的生成发生在初始化后期,@Transactional是可以放在接口上的,@Transactional只有放在public方法上才生效,最终使用的是cglib代理而不是jdk动态代理。

调用时序图

调用主要围绕TransactionInterceptor展开,around事务业务方法进行一些处理,例如before method时获得TransactionAttributeSource、确定具体的TransactionManager、根据事务上下文和传播行为决定是否创建新的事务等、绑定资源、解绑资源。

确定学习目标

遇到问题

EnableTransaction使用默认的PROXY基于接口的代理,为啥不实现接口@Transactonal也可以用 详见下面@EnableTransactionManagement源码章节

每次都要学rollbackFor,怎么全局写 可以自定义事务注解

@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Transactional(rollbackFor=Exception.class)public@interfaceDefaultTransactional{}

心存疑问/好奇

@EnableTransactionManagement属性的影响? 详见下面“@EnableTransactionManagement”源码章节

如何注入被事务代理的bean? 详见下面“事务代理bean的注入”源码章节

Spring声明式事务是如何实现的? 详见下面“Spring声明式事务的具体实现”章节

Spring如何实现对资源的管理(创建、重用和清理) 详见下面“Spring如何实现对资源的管理”源码章节

Spring如何处理物理事务和逻辑事务,不同的propagation是怎么处理的 详见下面“Spring如何处理物理事务和逻辑事务,不同的propagation是怎么处理的”源码章节

@TransactionalEventListeners是怎么实现的 详见下面“@TransactionalEventListeners是怎么实现的”源码章节

事务切面和普通定义的@Aspect切面的先后顺序 详见下面“事务切面和普通定义的@Aspect切面的先后顺序”源码章节

学习目标涉及的源码

@EnableTransactionManagement

使用的还是核心中的代码示例

属性影响

整体可以看出@EnableTransactionManagement会影响全局的APC选择,还会影响 transactionAdvisor的顺序,方便处理存在多个advisor。

为啥可以不实现接口@Transactonal也可以用

至于为啥@EnableTransactionManagement默认AdviceMode.PROXY、proxyTargetClass=false不实现接口还能使用@Transactional是因为最终选择的APC为AnnotationAwareAspectJAutoProxyCreator。

为啥不在Application上注解@EnableTransactionManagement也会使用上事务

按照上面的时序图调试就可以发现,AdviceModeImportSelector#selectImports的入参不一样,加的话如上面的代理示例@Configuration就是我们自己写的Application,不加的话@Configuration就是TransactionAutoConfiguration springboot自动装配的。

事务代理bean的注入

发现和普通的注入类似。注入的tryService是提前被代理好的bean。

Spring声明式事务的具体实现

结合上面的创建代理时序图、调用时序图、@EnableTransactionManagement时序图,我们来大体梳理下。

首先需要在@Configuration上使用@EnableTransactionManagement注解,不管是在自动装配还是在你自己的@Configuration。这样相当于xml的<tx:annotation-driven/>配置,表示启用基于事务注解的事务行为。同时确定了APC为AnnotationAwareAspectJAutoCreator、事务管理配置为ProxyTransactionManagementConfiguration。

ProxyTransactionManagementConfiguration确定了advisor为BeanFactoryTransactionAttributeSourceAdvisor、advice为TransactionInterceptor、事务属性资源AnnotationTransactionAttributeSource。这样就能使用BeanFactoryTransactionAttributeSourceAdvisor生成事务代理。

TransactionInterceptor设置了TransactionManager对事务进行管理,TransactionManager会使用到TransactionSynchronizationManager对资源进行同步。

Spring如何实现对资源的管理

首先要明白管理的对象是谁,下面用JDK原生写一个执行sql

Class.forName("com.mysql.jdbc.Driver");//加载驱动conn=DriverManager.getConnection(url);//获取数据库连接stmt=conn.createStatement();//创建执行环境stmt.execute(sql);//执行SQL语

主要就是对Connection的管理,创建连接、重用连接、清理连接。具体就是TransactionManager和TransactionSynchronizationManager的配合,具体可以根据上面的调用时序图进行了解。

Spring如何处理物理事务和逻辑事务,不同的propagation是怎么处理的

Spring事务底层是依赖于例如mysql之类的物理事务,一个Connection开启一个物理事务,逻辑事务是Spring根据Propagation来决定当前事务的行为(比如要挂起当前事务开启新的事务,还是加入当前事务),根据业务逻辑控制物理事务。一个物理事务对应一个Connection,一个Connection可能存在多个逻辑事务。

默认的Propagation.REQUIRED处理在上面的“调用时序图”可以了解到,下面将对常用的Propagation.PROPAGATION_NESTED传播行为进行讲解,通过对比默认的传播行为来更加清楚Spring对逻辑事务的处理,同时也能对事务回滚了解一二。具体如下图:

可以看到PROPAGATION_NESTED不会创建一个新的事务会对现有事务进行处理,更改TransactionStatus属性特别是savepoint,最后执行底层数据的“ROLLBACK TO SAVEPOINT”语句进行局部回滚(说明此种传播行为需要底层数据的支持),然后回到外层invokeWithTransaction,如果外层事务不想回滚直接catch住就可以了。

@TransactionalEventListeners是怎么实现的

TransactionalEventListener是一种ApplicationEventMulticaster会注册到TransactionSynchronizationManager中,TransactionSynchronizationManager提供一些事务钩子便可以根据事务不同阶段执行你发布的TransactionalEvent。

事务切面和普通定义的@Aspect切面的先后顺序

原始bean初始化之后会调用BeanFactory的applyBeanPostProcessorsAfterInitialization看是否有合适被代理,通过AnnotationAwareAspectJAutoProxyCreator找到所有拦截器(BeanFactoryTransactionAttributeSourceAdvisor 、InstantiationModelAwarePointcutAdvisor、ExposeInvacationInterceptor)并根据advisor的order进行排序生成一个具有多个advisor的代理。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Spring/4455.html