Transaction Management源码阅读路径
Posted qq_23473123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Transaction Management源码阅读路径相关的知识,希望对你有一定的参考价值。
前言
本文主要记录笔者学习Transaction Management的学习路径,读者可以对比自己的学习路径,一起讨论出探讨出更优学习路径。
基本路径
- 找到官方文档。
- 找到Overview页面,了解项目提供的能力、能实现的效果、设计理念、历史、最低要求等等。
- 找到Quick Start,快速把它的环境搭建起来,对项目有个感性的认知。
- 找到想了解的模块
- 找到模块的Introduction,一般这里有项目的基本介绍,你需要找到这个模块用到的基本概念或者名词介绍文档。
- 使用场景、功能特性以及相关生态系统介绍
- 了解核心/共用/复用模块
- 确定学习目标(遇到的问题、心存疑问/好奇),以点带面的阅读方式来阅读源码
- 提炼升华
- 结合实际开发/项目,可以解决什么问题、原来处理有什么不足/缺陷
- 日常思考、联想、完善
接下来将根据基本路径展开,源码时序图只画出核心点,具体还需读者自行调试。
Spring Framework Overview
Quick Start
想要了解的模块
使用场景和功能特性
Spring Framework全面支持事务,为事务管理提供了一致性的抽象。跨不同事务API的一致性编程模型。支持声明式事务管理。与Spring的数据库访问抽象的完美集成。
核心
通过阅读前面文章,使用声明式事务的话,可以大概知道Spring Framework的核心有事务策略的抽象-PlatformTransactionManager、管理每个线程的资源和事务同步的中央委托-TransactionSychrnoizationManager、使用AOP代理包装合适的bean的一种BeanPostProcessor-AbstractAutoProxyCreator、配合PlatformTransactionManager实现驱动事务around method的TransactionInterceptor。
下面将通过一个简单的事务示例(使用mysql、mybatis-plus、spring-boot),画一个时序图来看看核心类起到的作用。
示例
import lombok.SneakyThrows;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
@MapperScan(basePackages = {"com.whf.spring.dao"})
public class Application {
@SneakyThrows
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
import com.whf.spring.service.business.TryService;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
class ApplicationTests {
@Autowired
private TryService tryService;
@Test
void testTransaction() {
tryService.test();
}
}
import com.whf.spring.annotation.SimpleAnnotation;
import com.whf.spring.service.UserService;
import com.whf.spring.service.business.TryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Scope
@Slf4j
public class TryServiceImpl implements TryService {
@Autowired
private UserService userService;
@Override
@SimpleAnnotation
@Transactional(rollbackFor = Exception.class)
public void test() {
log.info("执行业务");
userService.test();
}
}
import com.whf.spring.dao.UserDao;
import com.whf.spring.entity.UserDo;
import com.whf.spring.service.UserService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserDo> implements UserService {
@Override
public void test() {
this.selectById(1);
throw new RuntimeException();
}
}
大致调用如下:
源码时序图
核心的顺序应该是:
- 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 @interface DefaultTransactional {
}
心存疑问/好奇
-
@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的代理。
以上是关于Transaction Management源码阅读路径的主要内容,如果未能解决你的问题,请参考以下文章
Spring Framework之Transaction Management