#
一、引子
在Spring中,事务有两种实现方式:
- 编程式事务管理: 编程式事务管理使用TransactionTemplate可实现更细粒度的事务控制。
- 申明式事务管理: 基于Spring AOP实现。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
申明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单(尤其是配合spring boot自动配置,可以说是精简至极!),且大部分业务都可以满足,推荐使用。
其实不管是编程式事务还是申明式事务,最终调用的底层核心代码是一致的。本章分别从编程式、申明式入手,再进入核心源码贯穿式讲解。
二、事务源码
2.1 编程式事务TransactionTemplate
编程式事务,Spring已经给我们提供好了模板类TransactionTemplate,可以很方便的使用,如下图:
"TransactionTemplate全路径名是:org.springframework.transaction.support.TransactionTemplate。看包名也知道了这是spring对事务的模板类。(spring动不动就是各种Template…),看下类图先:
"一看,哟西,实现了TransactionOperations、InitializingBean这2个接口(熟悉spring源码的知道这个InitializingBean又是老套路),我们来看下接口源码如下:
1 | 1 public interface TransactionOperations { |
如上图,TransactionOperations这个接口用来执行事务的回调方法,InitializingBean这个是典型的spring bean初始化流程中(飞机票:Spring IOC(四)总结升华篇)的预留接口,专用用来在bean属性加载完毕时执行的方法。
回到正题,TransactionTemplate的2个接口的impl方法做了什么?
1 | 1 |
如上图所示,实际上afterPropertiesSet只是校验了事务管理器不为空,execute()才是核心方法,execute主要步骤:
1.getTransaction()获取事务,源码见3.3.1
2.doInTransaction()执行业务逻辑,这里就是用户自定义的业务代码。如果是没有返回值的,就是doInTransactionWithoutResult()。
3.commit()事务提交:调用AbstractPlatformTransactionManager的commit,rollbackOnException()异常回滚:调用AbstractPlatformTransactionManager的rollback(),事务提交回滚,源码见3.3.3
2.2 申明式事务@Transactional
1.AOP相关概念
申明式事务使用的是spring AOP,即面向切面编程。AOP核心概念如下:
- 通知(Advice):定义了切面(各处业务代码中都需要的逻辑提炼成的一个切面)做什么what+when何时使用。例如:前置通知Before、后置通知After、返回通知After-returning、异常通知After-throwing、环绕通知Around.
- 连接点(Joint point):程序执行过程中能够插入切面的点,一般有多个。比如调用方式时、抛出异常时。
- 切点(Pointcut):切点定义了连接点,切点包含多个连接点,即where哪里使用通知.通常指定类+方法 或者 正则表达式来匹配 类和方法名称。
- 切面(Aspect):切面=通知+切点,即when+where+what何时何地做什么。
- 引入(Introduction):允许我们向现有的类添加新方法或属性。
- 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。
2.申明式事务
申明式事务整体调用过程,可以抽出2条线:
1.使用代理模式,生成代理增强类。
2.根据代理事务管理配置类,配置事务的织入,在业务方法前后进行环绕增强,增加一些事务的相关操作。例如获取事务属性、提交事务、回滚事务。
过程如下图:
"申明式事务使用@Transactional这种注解的方式,那么我们就从springboot 容器启动时的自动配置载入(spring boot容器启动详解)开始看。在/META-INF/spring.factories中配置文件中查找,如下图:
"
载入2个关于事务的自动配置类:
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,
jta咱们就不看了,看一下TransactionAutoConfiguration这个自动配置类:
1 | 1 |
TransactionAutoConfiguration这个类主要看:
- 2个类注解
@ConditionalOnClass(PlatformTransactionManager.class)
即类路径下包含PlatformTransactionManager
这个类时这个自动配置生效,这个类是spring事务的核心包,肯定引入了。
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
,这个配置在括号中的4个配置类后才生效。
- 2个内部类
TransactionTemplateConfiguration
事务模板配置类:
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
当能够唯一确定一个PlatformTransactionManager
bean时才生效。
@ConditionalOnMissingBean
如果没有定义TransactionTemplate
bean生成一个。
EnableTransactionManagementConfiguration
开启事务管理器配置类:
@ConditionalOnBean(PlatformTransactionManager.class)
当存在PlatformTransactionManager
bean时生效。
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
当没有自定义抽象事务管理器配置类时才生效。(即用户自定义抽象事务管理器配置类会优先,如果没有,就用这个默认事务管理器配置类)
EnableTransactionManagementConfiguration
支持2种代理方式:
- 1.JdkDynamicAutoProxyConfiguration:
@EnableTransactionManagement(proxyTargetClass = false)
,即proxyTargetClass = false
表示是JDK动态代理支持的是:面向接口代理。
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
,即spring.aop.proxy-target-class=false
时生效,且没有这个配置不生效。
- 2.CglibAutoProxyConfiguration:
@EnableTransactionManagement(proxyTargetClass = true)
,即proxyTargetClass = true
标识Cglib代理支持的是子类继承代理。@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
,即spring.aop.proxy-target-class=true
时生效,且没有这个配置默认生效。
注意了,默认没有配置,走的Cglib代理。说明@Transactional注解支持直接加在类上。
好吧,看了这么多配置类,终于到了@EnableTransactionManagement这个注解了。
1 | 1 (ElementType.TYPE) |
重点看类注解@Import(TransactionManagementConfigurationSelector.class)
TransactionManagementConfigurationSelector
类图如下:
如上图所示,TransactionManagementConfigurationSelector继承自AdviceModeImportSelector实现了ImportSelector接口。
1 | 1 public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { |
如上图,最终会执行selectImports方法导入需要加载的类,我们只看proxy模式下,载入了AutoProxyRegistrar、ProxyTransactionManagementConfiguration2个类。
- AutoProxyRegistrar:
给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
- ProxyTransactionManagementConfiguration:就是一个配置类,定义了事务增强器。
AutoProxyRegistrar
先看AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,复写registerBeanDefinitions方法,源码如下:
1 | 1 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { |
代理模式:AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
最终调用的是:registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
基础构建增强自动代理构造器
1 | 1 private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { |
如上图,APC_PRIORITY_LIST列表如下图:
1 | 1 /** |
如上图,由于InfrastructureAdvisorAutoProxyCreator这个类在list中第一个index=0,requiredPriority最小,不进入,所以没有重置beanClassName,啥都没做,返回null.
那么增强代理类何时生成呢?
InfrastructureAdvisorAutoProxyCreator类图如下:
"如上图所示,看2个核心方法:InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation实例化前+BeanPostProcessor接口的postProcessAfterInitialization初始化后。关于spring bean生命周期飞机票:Spring IOC(四)总结升华篇
1 | 1 |
通过追踪,由于InfrastructureAdvisorAutoProxyCreator是基础构建类,
advisedBeans.put(cacheKey, Boolean.FALSE)
添加进advisedBeans ConcurrentHashMap<k=Object,v=Boolean>标记是否需要增强实现,这里基础构建bean不需要代理,都置为false,供后面postProcessAfterInitialization实例化后使用。
我们再看postProcessAfterInitialization源码如下:
1 | 1 |
下面看核心方法createProxy如下:
1 | 1 protected Object createProxy( |
最终我们生成的是CGLIB代理类.到此为止我们分析完了代理类的构造过程。
ProxyTransactionManagementConfiguration
下面来看ProxyTransactionManagementConfiguration:
1 | 1 |
核心方法:transactionAdvisor()事务织入
定义了一个advisor,设置事务属性、设置事务拦截器TransactionInterceptor、设置顺序。核心就是事务拦截器TransactionInterceptor。
TransactionInterceptor使用通用的spring事务基础架构实现“声明式事务”,继承自TransactionAspectSupport类(该类包含与Spring的底层事务API的集成),实现了MethodInterceptor接口。spring类图如下:
"事务拦截器的拦截功能就是依靠实现了MethodInterceptor接口,熟悉spring的同学肯定很熟悉MethodInterceptor了,这个是spring的方法拦截器,主要看invoke方法:
1 | 1 |
如上图TransactionInterceptor复写MethodInterceptor接口的invoke方法,并在invoke方法中调用了父类TransactionAspectSupport的invokeWithinTransaction()方法,源码如下:
1 | 1 protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) |
如上图,我们主要看第一个分支,申明式事务,核心流程如下:
1.createTransactionIfNecessary()
:如果有必要,创建事务
2.InvocationCallback的proceedWithInvocation()
:InvocationCallback是父类的内部回调接口,子类中实现该接口供父类调用,子类TransactionInterceptor中invocation.proceed()。回调方法执行
3.异常回滚completeTransactionAfterThrowing()
1.createTransactionIfNecessary():
1 | 1 protected TransactionInfo createTransactionIfNecessary( |
核心就是:
1)getTransaction(),根据事务属性获取事务TransactionStatus,大道归一,都是调用PlatformTransactionManager.getTransaction(),源码见3.3.1。
2)prepareTransactionInfo(),构造一个TransactionInfo事务信息对象,绑定当前线程:ThreadLocal
2.invocation.proceed()回调业务方法:
最终实现类是ReflectiveMethodInvocation,类图如下:
"如上图,ReflectiveMethodInvocation类实现了ProxyMethodInvocation接口,但是ProxyMethodInvocation继承了3层接口…ProxyMethodInvocation->MethodInvocation->Invocation->Joinpoint
Joinpoint:连接点接口,定义了执行接口:Object proceed() throws Throwable; 执行当前连接点,并跳到拦截器链上的下一个拦截器。
Invocation:调用接口,继承自Joinpoint,定义了获取参数接口: Object[] getArguments();是一个带参数的、可被拦截器拦截的连接点。
MethodInvocation:方法调用接口,继承自Invocation,定义了获取方法接口:Method getMethod(); 是一个带参数的可被拦截的连接点方法。
ProxyMethodInvocation:代理方法调用接口,继承自MethodInvocation,定义了获取代理对象接口:Object getProxy();是一个由代理类执行的方法调用连接点方法。
ReflectiveMethodInvocation:实现了ProxyMethodInvocation接口,自然就实现了父类接口的的所有接口。获取代理类,获取方法,获取参数,用代理类执行这个方法并且自动跳到下一个连接点。
下面看一下proceed方法源码:
1 | 1 |
咱们这里最终调用的是((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);就是TransactionInterceptor事务拦截器回调 目标业务方法(addUserBalanceAndUser)。
3.completeTransactionAfterThrowing()
最终调用AbstractPlatformTransactionManager的rollback(),提交事务commitTransactionAfterReturning()最终调用AbstractPlatformTransactionManager的commit(),源码见3.3.3
总结
可见不管是编程式事务,还是声明式事务,最终源码都是调用事务管理器的PlatformTransactionManager接口的3个方法:
- getTransaction
- commit
- rollback
下一节我们就来看看这个事务管理如何实现这3个方法。
三、事务核心源码
咱们看一下核心类图:
"如上提所示,PlatformTransactionManager顶级接口定义了最核心的事务管理方法,下面一层是AbstractPlatformTransactionManager抽象类,实现了PlatformTransactionManager接口的方法并定义了一些抽象方法,供子类拓展。最后下面一层是2个经典事务管理器:
1.DataSourceTransactionmanager
,即JDBC单数据库事务管理器,基于Connection实现,
2.JtaTransactionManager
,即多数据库事务管理器(又叫做分布式事务管理器),其实现了JTA规范,使用XA协议进行两阶段提交。
我们这里只看基于JDBC connection的DataSourceTransactionmanager
源码。
PlatformTransactionManager接口:
1 | 1 public interface PlatformTransactionManager { |
1. getTransaction获取事务
"AbstractPlatformTransactionManager实现了getTransaction()方法如下:
1 | 1 |
如上图,源码分成了2条处理线,
1.当前已存在事务:isExistingTransaction()判断是否存在事务,存在事务handleExistingTransaction()根据不同传播机制不同处理
2.当前不存在事务: 不同传播机制不同处理
handleExistingTransaction()源码如下:
1 | 1 private TransactionStatus handleExistingTransaction( |
如上图,当前线程已存在事务情况下,新的不同隔离级别处理情况:
1.NERVER:不支持当前事务;如果当前事务存在,抛出异常:”Existing transaction found for transaction marked with propagation ‘never’”
2.NOT_SUPPORTED:不支持当前事务,现有同步将被挂起:suspend()
3.REQUIRES_NEW挂起当前事务,创建新事务:
1)suspend()
2)doBegin()
4.NESTED嵌套事务
1)非JTA事务:createAndHoldSavepoint()创建JDBC3.0保存点,不需要同步
2) JTA事务:开启新事务,doBegin()+prepareSynchronization()需要同步
这里有几个核心方法:挂起当前事务suspend()、开启新事务doBegin()。
suspend()源码如下:
1 | 1 protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException { |
doSuspend(),挂起事务,AbstractPlatformTransactionManager抽象类doSuspend()会报错:不支持挂起,如果具体事务执行器支持就复写doSuspend(),DataSourceTransactionManager实现如下:
1 | 1 |
挂起DataSourceTransactionManager事务的核心操作就是:
1.把当前事务的connectionHolder数据库连接持有者清空。
2.当前线程解绑datasource.其实就是ThreadLocal移除对应变量(TransactionSynchronizationManager类中定义的private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<Map<Object, Object>>("Transactional resources");)
TransactionSynchronizationManager事务同步管理器,该类维护了多个线程本地变量ThreadLocal,如下图:
1 | 1 public abstract class TransactionSynchronizationManager { |
doBegin()源码如下:
1 | 1 |
如上图,开启新事务的准备工作doBegin()的核心操作就是:
1.DataSourceTransactionObject“数据源事务对象”,设置ConnectionHolder,再给ConnectionHolder设置各种属性:自动提交、超时、事务开启、隔离级别。
2.给当前线程绑定一个线程本地变量,key=DataSource数据源 v=ConnectionHolder数据库连接。
2. commit提交事务
一、资源管理类
讲解源码之前先看一下资源管理类:SqlSessionSynchronization是SqlSessionUtils的一个内部类,继承自TransactionSynchronizationAdapter抽象类,实现了事务同步接口TransactionSynchronization。
类图如下:
"TransactionSynchronization接口定义了事务操作时的对应资源的(JDBC事务那么就是SqlSessionSynchronization)管理方法:
1 | 1 // 挂起事务 |
后续很多都是使用这些接口管理事务。
二、 commit提交事务
"AbstractPlatformTransactionManager的commit源码如下:
1 | 1 |
如上图,各种判断:
- 1.如果事务明确标记为本地回滚,-》执行回滚
- 2.如果不需要全局回滚时提交 且 全局回滚-》执行回滚
- 3.提交事务,核心方法processCommit()
processCommit如下:
1 | 1 private void processCommit(DefaultTransactionStatus status) throws TransactionException { |
如上图,commit事务时,有6个核心操作,分别是3个前置操作,3个后置操作,如下:
1.prepareForCommit(status);源码是空的,没有拓展目前。
2.triggerBeforeCommit(status); 提交前触发操作
1 | 1 protected final void triggerBeforeCommit(DefaultTransactionStatus status) { |
triggerBeforeCommit源码如下:
1 | 1 public static void triggerBeforeCommit(boolean readOnly) { |
如上图,TransactionSynchronizationManager类定义了多个ThreadLocal(线程本地变量),其中一个用以保存当前线程的事务同步:
1 | private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations"); |
遍历事务同步器,把每个事务同步器都执行“提交前”操作,比如咱们用的jdbc事务,那么最终就是SqlSessionUtils.beforeCommit()->this.holder.getSqlSession().commit();提交会话。(源码由于是spring管理实务,最终不会执行事务提交,例如是DefaultSqlSession:执行清除缓存、重置状态操作)
3.triggerBeforeCompletion(status);完成前触发操作,如果是jdbc事务,那么最终就是,
SqlSessionUtils.beforeCompletion->TransactionSynchronizationManager.unbindResource(sessionFactory);
解绑当前线程的会话工厂
this.holder.getSqlSession().close();关闭会话。(源码由于是spring管理实务,最终不会执行事务close操作,例如是DefaultSqlSession,也会执行各种清除收尾操作)
4.triggerAfterCommit(status);提交事务后触发操作。TransactionSynchronizationUtils.*triggerAfterCommit();*->TransactionSynchronizationUtils.invokeAfterCommit
,如下:
1 | 1 public static void invokeAfterCommit(List<TransactionSynchronization> synchronizations) { |
好吧,一顿找,最后在TransactionSynchronizationAdapter中复写过,并且是空的….SqlSessionSynchronization继承了TransactionSynchronizationAdapter但是没有复写这个方法。
5.triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
TransactionSynchronizationUtils.TransactionSynchronizationUtils.invokeAfterCompletion
,如下:
1 | 1 public static void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) { |
afterCompletion:对于JDBC事务来说,最终:
1)如果会话任然活着,关闭会话,
2)重置各种属性:SQL会话同步器(SqlSessionSynchronization)的SQL会话持有者(SqlSessionHolder)的referenceCount引用计数、synchronizedWithTransaction同步事务、rollbackOnly只回滚、deadline超时时间点。
6.cleanupAfterCompletion(status);
1)设置事务状态为已完成。
2) 如果是新的事务同步,解绑当前线程绑定的数据库资源,重置数据库连接
3)如果存在挂起的事务(嵌套事务),唤醒挂起的老事务的各种资源:数据库资源、同步器。
1 | 1 private void cleanupAfterCompletion(DefaultTransactionStatus status) { |
对于DataSourceTransactionManager,doCleanupAfterCompletion源码如下:
1 | 1 protected void doCleanupAfterCompletion(Object transaction) { |
3. rollback回滚事务
"AbstractPlatformTransactionManager中rollback源码如下:
1 | 1 public final void rollback(TransactionStatus status) throws TransactionException { |
processRollback源码如下:
1 | 1 private void processRollback(DefaultTransactionStatus status) { |
如上图,有几个公共方法和提交事务时一致,就不再重复。
这里主要看doRollback,DataSourceTransactionManager的doRollback()源码如下:
1 | 1 protected void doRollback(DefaultTransactionStatus status) { |
好吧,一点不复杂,就是Connection的rollback.
四、时序图
特地整理了时序图(简单的新事务,没有画出保存点等情况)如下:
"