本文目录
- 更新内容
- 前言
- spring事务特性简述
- 研究 Spring 事务源码入口
- AutoProxyRegistrar
- ProxyTransactionManagementConfiguration(事务源码核心入口链)
- 小结 @EnableTransactionManagement
- 常见问题数据回滚、事务异常案例分析
- 问题一:事务在什么情况下会失效?
- 问题二:子事务的回滚原理了解吗?
- 事务拦截器了解吗?(TransactionInterceptor )
- 事务信息对象的创建了解吗?(createTransactionIfNecessary)
- 事务传播行为原理了解吗?(getTransaction)
- Spring 支持的传播行为简述
- 传播行为 MANDATORY 原理剖析
- 传播行为 REQUIRED 原理剖析
- 数据源事务对象是什么?(doGetTransaction)
- 分析doGetTransaction之getResource()
- 分析getTransaction之startTransaction()
- 分析startTransaction之doBegin()重点
- 分析getTransaction之handleExistingTransaction()
- completeTransactionAfterThrowing()
- 分析completeTransactionAfterThrowing之rollback()
- 分析rollback之processRollback
- 分析processRollback之doRollback()重点
- commitTransactionAfterReturning(txInfo);
- 分析commitTransactionAfterReturning之commit()重点
- 分析commit之processCommit()
- 嵌套事务简化版执行流程
- 事务失效场景具体分析
- REQUIRES_NEW事务失效情况分析
- Propagation.NESTED事务失效情况分析
- invokeWithinTransaction伪代码
更新内容
- 文章排版优化中~,文章是几年前写的,排版有点乱
- 新增面试专栏,结合常见的面试问题做一个总结,等文章优化完再加上去,会持续更新的,2023-02-20记。
前言
说起 Spring 事务大家肯定都不陌生,Springboot 项目中我们只需在需要事务控制的方法上面添加 @Transactional 注解即可,接下来本文来剖析其运行原理。
spring事务特性简述
- 支持配置传播行为
- 支持配置事务隔离级别
- 支持按照指定异常回滚
研究 Spring 事务源码入口
把 @EnableTransactionManagement 作为我们研究 Spring 事务源码的入口,里面 Import 了一个 TransactionManagementConfigurationSelector 组件。
而 TransactionManagementConfigurationSelector 其实就是一个 ImportSelector,为 IOC 容器中注册了俩个 bean,其一是 AutoProxyRegistrar (启动入口类),其二是 ProxyTransactionManagementConfiguration (事务的切面、拦截器配置类),要搞明白其原理接着剖析这俩个类就行。
AutoProxyRegistrar
点进去 AutoProxyRegistrar 最终会发现,里面的注册逻辑和 @EnableAspectJAutoProxy 里面的逻辑是一模一样,我们暂时只需要知道 AutoProxyRegistrar 为我们注册了一个入口类即可。
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
/**
* AUTO_PROXY_CREATOR_BEAN_NAME:internalAutoProxyCreator
*/
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
/**
* 容器中存在的入口类
*/
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
/**
* cls:当前要注册的入口类
* AnnotationAwareAspectJAutoProxyCreator优先级最高
* requiredPriority:当前要注册入口类的优先级
*/
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
/**
* 注册当前传进来的入口类
*/
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
ProxyTransactionManagementConfiguration(事务源码核心入口链)
ProxyTransactionManagementConfiguration 点进去,发现此类帮我们注册了三个 bean ,这三个 bean 的作用分别阐述一下就是
- TransactionInterceptor:事务拦截器, Aop 责任链中的事务源码核心入口链就是指此链。
- TransactionAttributeSource:搜集 @Transaction 注解上面的所有属性,封装成一个TransactionAttributeSource 对象。
- BeanFactoryTransactionAttributeSourceAdvisor:注册了一个advisor(事务切面,Spring 会为 Advisor 类型的对象生成代理对象)
事务切面(advisor),advisor 中织入了通知(@before、@after…),在每次调用事务方法之前先是执行事务通知中的逻辑,继而才是执行目标方法。
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// 给容器中注册了一个bean:名字为 BeanFactoryTransactionAttributeSourceAdvisor
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
// 生成一个事务的切面
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
// 搜集@Transaction:注解上面的所有属性
advisor.setTransactionAttributeSource(transactionAttributeSource);
// 设置事务通知,涉及到aop调用链
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
//bean 作用:搜集@Transaction:注解上面的所有属性
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
// bean 作用:设置事务通知,涉及到aop调用链
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
// 事务管理器
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
小结 @EnableTransactionManagement
@EnableTransactionManagement 见名知意:开启事务管理器,其实就是注册一下事务运行所必须依赖的 bean,好比汽车的行驶少不了发动机的帮助,事务的运行同样需要依赖很多小组件的支持。Spring 事务的入口源码已经剖析完成。接下来简单说一下其运用。
常见问题数据回滚、事务异常案例分析
本着带着业务问题看源码的原则,也为了帮助读者更好了理解 Spring 事务,本文的编写脉络是按使用 Spring 事务发现问题,然后带着问题去看源码这种顺序去编写的。
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserServiceImpl userService;
@Transactional(propagation = Propagation.NEW)
public void a() {
this.update(new UpdateWrapper<User>().eq("id", 52).set("age", 100));
System.out.println("a事务更新完毕");
userService.b();
userService.c();
userService.d();
}
}
上述的 UserServiceImpl 就是一个事务切面 ,其中的 b、c、d 方法上都标注了@Transactional(propagation = Propagation.NEW) ,且方法里面依次更新 id 为 53、54、55 这些行的数据并 set age = 100。
编写测试类,当我们启动项目的时候访问我们的 UserController 中的 test()接口继而触发 userService.a(); 就会依次执行更新操作,id从 52 - 55 的行数据的 age 都会为100。
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserServiceImpl userService;
@GetMapping("/test")
public String test() {
userService.a();
return "ok";
}
那如果我们在事务c这里抛出一个异常,其他事务依旧是原样的,执行的结果又会是怎么样呢?结果就是 b 更新成功,a、c、d更新失败。
- what fark you?不是说 REQUIRES_NEW 每次都是新建的一个事务的嘛!你c事务抛出异常关事务 a、c、d什么事讷?按理来说不是只有c更新失败,a、b、d更新成功嘛。
通过分析如上案例,我们发现了俩个经典问题,事务居然会失效、子事务回滚会异常诶,难道是 Spring 出现了 bug,还是我们要背锅?
问题一:事务在什么情况下会失效?
由于 b、c、d 方法都是同一个类下面的,可能很多人会和入下图这样去调用方法?这样是不行的!这样调用 b、c、d 上面的 @Transactional 注解都会失效。b、c、d 都没有走 UserServiceImpl 这个事务切面,从而导致事务会失效。正确的调用应该是 UserServiceImpl.a() (先是走代理对象中的逻辑,继而执行目标方法)这种方式去调用,而不是 a()这样直接调用(只是单纯的执行代码逻辑).
问题二:子事务的回滚原理了解吗?
这个问题需看完源码才能更好的理解,开始看源码,从下图断点,进去发现和 Aop 的执行流程一模一样。搞懂了 Aop 的运行流程,对于搞懂事务的运行流程还是很轻松的。只是可能事务里面的逻辑大家还是不是很清楚而已。
userService.a() 的执行本质是调用 Aop 拦截器链,遍历代理对象中的拦截器链,依次执行对应的逻辑。
遍历调用拦截器链中的 invoke()方法,换言之就是执行 a()前会先执行事务切面中的事务通知逻辑,接着执行目标方法中的逻辑,最后执行后置通知等等,按照一定顺序去执行。
这个拦截器链 最终会遍历到 TransactionInterceptor(Aop 责任链中的事务源码核心入口链)。继而执行 invoke()方法 ,这里面就是我们的 Spring 事务的调用源码了。
我直接一句好家伙,这一团密密麻麻的是个什么鬼东西,这个是我用 maven 依赖 debug 时的Spring 最新的源码,我以下贴的带注释的源码是基于 Spring 5.0源码来分析的。原理都一Spring5.0 源码我自己写了注释,最新的没有克隆编译写注释。
事务拦截器了解吗?(TransactionInterceptor )
事务拦截器即指的是 TransactionInterceptor ,其核心逻辑在 TransactionAspectSupport 里面,当事务切面被执行的时候,通过遍历其AOP 责任链最终遍历到我们的事务拦截器,处理与事务有关的逻辑,比如说数据库连接的创建、设置是否自动 commit ,解析 @Transaction 注解属性封装成 TransactionAttribute ,异常的回滚逻辑等等,简单点来说就是用代码的形式封装 db 命令行那套操作事务的逻辑。
Spring 5.0 中的 invokeWithinTransaction()源码里面的逻辑很多,挑重点分析即可。我们对事务的应用层面,用到的最核心的功能无非就是,事务的回滚,提交这俩个,看源码也是同样的道理,看对应的源码即可。下面是我摘出来的四大核心步骤。
- createTransactionIfNecessary;开启事务
- retVal = invocation.proceedWithInvocation();执行目标方法中的逻辑
- completeTransactionAfterThrowing(txInfo, ex);事务回滚
- commitTransactionAfterReturning(txInfo);事务提交
事务信息对象的创建了解吗?(createTransactionIfNecessary)
事务对象:指的是 TransactionInfo 这个类,和 Spring 中的 BeanDefin 这个类一样,BeanDefin 是对类属性的一个封装,而 TransactionInfo 是对 事务管理器(TransactionAttribute)、事务状态对象(TransactionStatus)@Transaction注解信息对象(TransactionAttribute)、数据源事务对象(DataSourceTransactionObject)等一切和 db 打交道信息的一个封装。通过 TransactionInfo 对象可以很轻易的实现 commit 、回滚、回滚点回滚、获取当前事务用到的数据源、获取回滚点,等一系列操作,因此你会发现不论是 Spring 源码中的提交、回滚事务方法,都需要 TransactionInfo 的传入做支撑。
图解 TransactionInfo 内部结构如下,结构都知道了,我们逐个分析每个小结构是如何被创建的即可。
知道了 TransactionInfo 对象是什么了,那么我们去剖析一下 TransactionInfo 到底是如何被创建的吧。接下来来到 createTransactionIfNecessary 方法中一探究竟。先是通过事务管理器获取 TransactionAttribute 对象,不为 null 继而执行 getTransaction 方法。去获取事务状态对象(TransactionStatus)。
TransactionStatus 与 TransactionInfo 的区别是什么?
TransactionInfo 包含 TransactionStatus 对象,而 TransactionStatus 最主要的功能就是标识当前事务是否是新建状态。true:新建,false:非新建
当事务状态对象准备好的时候,最后会返回一个事务信息对象,同时会通过 ThrealLocal 将事务信息对象与线程做一个绑定,好处就是在同一个线程中的任何地方都可以获取到当前的事务信息对象。
建立事务信息对象 TransactionInfo 与线程的一一绑定关系
ThreadLocal 在事务源码中的体现
事务传播行为原理了解吗?(getTransaction)
getTransaction中的重要代码我都在图中绿色 下划线标注出来了,下面对其做个大概的解释,详细源码下文分析
- doGetTransaction:获取事务对象(每个事务的事务对象都是不同的,但是每个事务的事务对象中的 contextholder 可能相同),大家面试被常问到的,哪家源码使用到了 ThreadLocal ,这里面就用到了
- handleExistingTransaction:判断当前事务对象是否是嵌套事务,如果是走嵌套事务执行逻辑
- startTransaction:非嵌套事务,且事务的传播行为为PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED,都是执行 startTransaction() ,其余的传播行为读者可以自行去分析一哈
为了方便读者阅读本文,贴一哈对应的传播行为对照表
Spring 支持的传播行为简述
为了方便读者更好的阅读下文,贴一张 Spring 支持的传播行为对照图。
传播行为 MANDATORY 原理剖析
下图 listByIds 方法使用的是 MANDATORY 传播行为,如果单独调用 listByIds 方法就会报错,由于是主事务的调用,此时会来到图一的逻辑里面,判断传播行为对应 2 就抛出异常了,但是通过调用 list 方法,则不会抛出异常因为,listByIds 是作为子事务执行的,直接走 handleExistingTransaction 里面的逻辑了,根本没机会执行 def.getPropagationBehavior() == 2 这段代码。
传播行为 REQUIRED 原理剖析
REQUIRED: 如果外层存在事务加入外层事务一块提交、一块回滚,否则新建一个事务。
为什么外层事务传播行为为 REQUIRED 时,会新开一个事务?
答;因为 REQUIRED 对应 0 ,如果是最外层方法是用的 REQUIRED 传播行为第一次走 getTransaction 去获取事务状态对象的时候会直接 startTransaction 开启一个新事务,而 startTransaction 里面的逻辑就是通过从 ConnectionHolder 中获取数据库一个新连接,然后设置自动 comit 为 false。
REQUIRED:为什么如果外层存在事务加入外层事务一块提交、一块回滚。
答:是这样的,子事务的传播行为为 REQUIRED 的时候,有一套独有的处理子事务的逻辑,这个时候子事务在创建事务状态对象 (TransactionStatus) 的时候,不会直接 startTransaction 开启一个新事务,而是直接 prepareTransactionStatus 构造一个事务状态对象(TransactionStatus),且标记为非新建状态,而这个事务状态对象的构造需要依赖数据源事务对象,而 Spring 在 获取数据源事务对象的时候,拿的是同一线程中的 ConnectionHondler ,ConnectionHondler 存储了上个事务中的连接,由于连接用的是同一个,当发生异常的时候,由于外层事务和子事务用的是一个数据库连接,就会一块提交、一块回滚了。
数据源事务对象是什么?(doGetTransaction)
不管是子事务还是相对于子事务最外层的主事务的运行,他们都离不开数据源的支持
- 每个事务状态对象(TransactionStatus),填充的永远是同一个线程中上个事务中的ConnectionHolder
@Override
protected Object doGetTransaction() {
//新建一个事务对象
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
//设置事务是否允许嵌套
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//从threadlocal中获取连接对象(ConnectionHolder),第一次获取到的ConnectionHolder为null,obtainDataSource():DataSourceTransactionManager中配置过的数据源
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
//事务对象中设置连接ConnectionHolder,ConnectionHolder中包含连接conection
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
分析doGetTransaction之getResource()
点进来其实就是doGetResource(),从ThrealLocal(dataSource,contextHolder)中获取connectionHolder对象,第一个事务获取到的contextHolder为null
private static Object doGetResource(Object actualKey) {
/**
* actualKey:DataSourceTransactionManager中配置过的数据源
* resources:ThreadLocal<Map<Object, Object>>
*/
Map<Object, Object> map = resources.get();
//第一次的contextHolder中数据库连接为null,数据库连接的填充在startTransaction里面
if (map == null) {
return null;
}
//这个map的 key:dataSource,value:contextHolder
Object value = map.get(actualKey);
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
//返回一个connectionHolder
return value;
}
分析getTransaction之startTransaction()
外层事务会走这里
- ⚠️在事务开启之前标记当前事务是一个新事务, 然后才是真正的doBegin()开启事务
- ⚠️提前剧透:通过startTransaction()方法开启的事务,状态、连接都是新的
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
/**
* getTransactionSynchronization:return this.transactionSynchronization(一个允许事务是否同步的标识符号)
* SYNCHRONIZATION_NEVER:事务永远不会同步
* 这俩个货不相等,那么开启一个新事务
*/
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
/**
* ⚠️:newTransaction属性为new
* DefaultTransactionStatus:事务状态对象,
* suspendedResources:上一个事务的ConnectionHolder
*/
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
/**
* startTransaction()->doBegin()
* 开启事务:其实也就是打开一个连接改变autoCommit的状态而已
*/
doBegin(transaction, definition);
/**
*
*/
prepareSynchronization(status, definition);
return status;
}
分析startTransaction之doBegin()重点
doBegin代码很长,精简流程描述如下
- 判断事务对象中的connectionHolder属性是否为 null,为null标志着嵌套事务的连接与外层事务所用的连接不是同一个,这个关系到事务的回滚问题
- 获取事务对象中connectionHolder中的连接,设置自动提交为false
- 标记事务对象为激活状态
- 数据源与connectionHolde形成映射关系与线程一一进行绑定,存入threalLocal()中
protected void doBegin(Object transaction, TransactionDefinition definition) {
/**
* 事务对象转型
*/
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
/**
* hasConnectionHolder: return(this.connectionHolder != null);
* isSynchronizedWithTransaction:return this.synchronizedWithTransaction;
*/
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
/**
* 从数据源中获取连接
*/
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
/**
* 给此时的事务对象设置一个ConnectionHolder进去,标记是一个新连接
*/
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
/**
* 走到这的事务一般事务传播行为都是Propagation.REQUIRES_NEW
* 设置当前的ConnectionHolder允许事务同步,也就是允许嵌套事务都是一个新的事务
*/
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
/**
* 获取当前事务的连接对象
*/
con = txObject.getConnectionHolder().getConnection();
/**
* 获取@Transaction注解中的事务隔离级别
*/
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
/**
* 给事务对象设置传播行为
*/
txObject.setPreviousIsolationLevel(previousIsolationLevel);
/**
* 给事务对象设置只读
*/
txObject.setReadOnly(definition.isReadOnly());
if (con.getAutoCommit()) {
/**
* 还原:自动提交为true
*/
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
/**
* 关闭事务自动提交
*/
con.setAutoCommit(false);
}
/**
* 与只读事务有关
*/
prepareTransactionalConnection(con, definition);
/**
* 给当前事务标记一个状态:活跃事务
*/
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
if (txObject.isNewConnectionHolder()) {
/**
* 将ConnectionHolder与数据源进行一一绑定
* key:DataSource
* value:ConnectionHolder
*/
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
} catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
/**
* 释放连接
*/
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
分析getTransaction之handleExistingTransaction()
嵌套事务会走下面的代码,读者现在大致浏览一下下面的代码即可,下文有细说
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
/**
* 重点:PROPAGATION_REQUIRES_NEW
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
/**
* 清空当前事务对象中的contextholder = null,执行这行代码说明,嵌套事务与原先事务使用的不是同一个连接对象
*/
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
/**
* 开启一个新事务
*/
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
} catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
/**
* 默认为true
*/
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
/**
* 设置不是最新的事务
*/
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
/**
* 创建保存点
*/
status.createAndHoldSavepoint();
/**
* ⚠️:直接return
*/
return status;
} else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
return startTransaction(definition, transaction, debugEnabled, null);
}
}
/**
* 默认的事务传播行为:PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.用的是同一个连接,且嵌套的事务为旧的事务
*/
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
/**
* 直接准备连接信息,没有开启事务这个环节
*/
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
事务传播行为的定义,结合源码来分析一下这些定义吧
PROPAGATION_NEVER:直接抛出异常,存在嵌套事务立马抛出异常。鸡肋
PROPAGATION_NOT_SUPPORTED:挂起外层事务,标记此事务不是最新的事务,不是最新的事务不会进行提交操作。个人感觉好鸡肋这个,你不加事务注解不也一样吗。
PROPAGATION_REQUIRES_NEW:此种状态嵌套事务先是将事务对象中的connectionHolder设置为null,为的是在startTransaction()的时候,为当前事务开辟一个新的连接。(也就是上图定义的每次开启新的事务)
PROPAGATION_NESTED:设置回滚点,标记当前事务为非最新的事务,如果某个子事务回滚,外层事务并没有吞掉子事务的异常,那么会引起外层事务回滚,由于所有事务都是用的一个连接,那么外层事务回滚的效果就是造成所有事务的全部回滚
回滚点的位置
PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED :默认传播行为,标记事务状态为非新的。
PROPAGATION_REQUIRED与PROPAGATION_SUPPORTS的区别在于:
- PROPAGATION_REQUIRED作用于外层事务下会进行startTransaction()操作(此时会开启一个全新的事务),作用于子事务只是标记事务状态为非新的
- 而PROPAGATION_SUPPORTS只作用于子事务,标记事务状态为非最新的事务,
只是标记事务状态不是新的,相当于还是在最外层的事务中进行执行。加入外层事务是这个意思
最外层事务直接会开启一个新的事务
completeTransactionAfterThrowing()
在进行火炬传递的期间出现异常,那么会进行回滚操作。我们直接来看看spring是怎么回滚的,点进去rollback()
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
/**
* 回滚异常
*/
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
分析completeTransactionAfterThrowing之rollback()
对事务状态进行了一个转型操作,接着processRollback(),点进去processRollback()接着看
@Override
public final void rollback(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
/**
* 回滚操作
*/
processRollback(defStatus, false);
}
分析rollback之processRollback
仔细观察spring的回滚源码,大体简化步骤分为如下几步
- 如果当前要回滚的事务状态中存在回滚点,按照回滚点回滚
- 如果当前要回滚的事务状态被标记为是一个新事务,那么doRollback(status)
- ⚠️其他情况标记事务状态中的 rollbackonly = false,这里标记的rollbackonly是存在于ResourceHoldersupport类!!!!!!!!
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
/**
* 如果有回滚点,按照回滚点回滚
*/
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
/**
* 如果是一个新的事务(存在事务且事务是最新的),回滚对应的这个新的事务中的DML语句
*/
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
if (status.hasTransaction()) {
/**
* isLocalRollbackOnly:return rollbackOnly回滚部分
* isGlobalRollbackOnParticipationFailure:参与失败造成的全部回滚 return this.globalRollbackOnParticipationFailure;
*/
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
/**
* 设置rollbackOnly属性为true,在最外层事务提交的时候会触发回滚操作
*/
doSetRollbackOnly(status);
} else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
} catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
} finally {
cleanupAfterCompletion(status);
}
}
分析processRollback之doRollback()重点
从事务状态中获取到连接进行回滚操作
@Override
protected void doRollback(DefaultTransactionStatus status) {
/**
* 事务状态中获取对应的连接
*/
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
/**
* 回滚对应连接中的数据
*/
con.rollback();
} catch (SQLException ex) {
throw translateException("JDBC rollback", ex);
}
}
commitTransactionAfterReturning(txInfo);
进行事务的提交操作,在这里面完成,点进去commit()
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
分析commitTransactionAfterReturning之commit()重点
⚠️在事务提交之前还有一次判断是否需要回滚的操作!,一般来说如果ResourceHolderSupport中的RollbackOnly属性被修改为true那么会进行回滚操作,反之接着才是提交操作
public final void commit(TransactionStatus status) throws TransactionException {
//事务已经提交过了抛出异常
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
/**
* AbstractTransactionStatus.RollbackOnly是否为true,默认是false
*/
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
//回滚操作
processRollback(defStatus, false);
return;
}
/**
* shouldCommitOnGlobalRollbackOnly()默认返回false
* isGlobalRollbackOnly():ResourceHolderSupport中的RollbackOnly属性为true那么也会进行回滚操作
*/
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
//回滚操作
processRollback(defStatus, true);
return;
}
/**
* 事务的提交
*/
processCommit(defStatus);
}
分析commit之processCommit()
我这里就不贴代码了,有回滚点抹掉回滚点不进行事务提交操作、只有是新事务才会来进行提交的,到此spring事务的整个执行流程算是完结撒花🎉了,别着急走,来看下下文的配套图文解说吧
嵌套事务简化版执行流程
事务失效场景具体分析
注意回滚操作是根据连接回滚的,commit的之前还有一次判断是否需要回滚。这俩点对于事务回滚的理解十分重要!。
@Transactional(propagation = Propagation.NEW)
public void a() {
this.update(new UpdateWrapper<User>().eq("id", 52).set("age", 100));
System.out.println("a事务更新完毕");
userService.b();
userService.c();
userService.d();
}
上述代码的运行流程用伪代码表示如下,嵌套事务简化版伪代码如下
有了这个伪代码,我们再来分析一下真实事务失效的场景吧!
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void a() {
this.update(new UpdateWrapper<User>().eq("id", 52).set("age", 100));
System.out.println("a事务更新完毕");
userService.b();
userService.c();
userService.d();
//REQUIRES_NEW事务传播行为 : a失败,b、c、d成功
//Propagation.NESTED事务传播行为 :a、b、c、d失败
//throw new NullPointerException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
this.update(new UpdateWrapper<User>().eq("id", 53).set("age", 100));
System.out.println("b事务更新完毕");
//REQUIRES_NEW事务传播行为 : a、b、c、d失败
//Propagation.NESTED事务传播行为 :a成功,b、c、d失败
//throw new NullPointerException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void c() {
this.update(new UpdateWrapper<User>().eq("id", 54).set("age", 100));
System.out.println("c事务更新完毕");
//REQUIRES_NEW事务传播行为 : b成功,a、c、d失败
//Propagation.NESTED事务传播行为 :a、b成功,c、d失败
//throw new NullPointerException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void d() {
this.update(new UpdateWrapper<User>().eq("id", 55).set("age", 100));
System.out.println("d事务更新完毕");
//REQUIRES_NEW事务传播行为 : b、c成功,a、d失败
//Propagation.NESTED事务传播行为 :a、b、c成功,d失败
//throw new NullPointerException();
}
REQUIRES_NEW事务失效情况分析
事务a、b、c、d执行回滚情况如下。
| |执行结果| a事务 |b事务 | c事务| d事务|
|–|–|–|–|–|–|–|
|抛出异常 | | | | | |
|a事务 | | x | y | y | y|
|b事务 | |x | x | x | x|
|c事务 | | x | y | x | x|
|d事务 | | x | y | y |x |
- REQUIRES_NEW传播行为下面a、b、c、d开辟的事务都拥有各自独立的连接。
b事务抛出异常,为什么a、b、c、d事务都会进行回滚呢?
b事务抛出的异常具有传递性连着会被a事务捕获,因此a、b会回滚。因为b事务抛出异常后直接代码都不会在接着向下执行了,因此c、d事务压根执行不到,因此a、b、c、d事务都更新失败。同理验证上述表格的所有情况
Propagation.NESTED事务失效情况分析
事务a、b、c、d执行回滚情况如下。Propagation.NESTED虽说也是同一个线程中的事务共享同一个连接,但是会创建保存点,回滚也是根据回滚点进行回滚的
- 如果不对a事务中的代码进行try{}catche(Exception e){}操作,那么无论是a抛出的异常,还是b、c、d抛出的异常也好,a、b、c、d事务都将会进行回滚,因为这四个事务使用的是同一个连接,
此表格针对上图这种情况分析的
| |执行结果| a事务 |b事务 | c事务| d事务|
|–|–|–|–|–|–|–|
|抛出异常 | | | | | |
|a事务 | | x | x | x | x|
|b事务 | |y | x | x | x|
|c事务 | | y | y | x | x|
|d事务 | | y | y | y |x |
为什么b事务抛出异常a事务不会回滚呢?为什么c、d事务回滚了?
- 答:b事务抛出异常向上抛被空的try-catch给吞掉了,虽然a、b事务中是同一个连接但是是根据回滚点来进行回滚的,因此不会触发a事务的回滚操作,而b事务一旦被抛出异常,c、d压根就不会被执行了,因此b事务抛出异常,只有a能更新成功
同理分析只有c事务抛出异常的情况下,为什么a、b更新成功,c、d更新失败呢?
- 答:a更新成功是因为,c将异常向上抛给a的时候,被a给吞掉了异常,并没有触发a事务对应的回滚操作
- 事务b更新成功是因为,c与b虽然是同一个连接,但是c回滚是根据回滚点回滚的,只回滚事务c的部分,d更新失败是压根就执行不到d中的代码了
invokeWithinTransaction伪代码
invokeWithinTransaction {
//获取@Transaction注解上的源属性
TransactionAttributeSource tas = getTransactionAttributeSource();
//tas理论上和txAttr差不多
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//获取事务管理器
final TransactionManager tm = determineTransactionManager(txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
TransactionInfo txInfo = createTransactionIfNecessary {
status = tm.getTransaction(txAttr);
{
// 获取一个事务对象,第一次获取到的事务对象里面并没有连接,且且ConnectionHolder = null
Object transaction = doGetTransaction();
{
//新建一个事务对象
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
//设置事务是否允许嵌套
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//从threadlocal中获取连接对象(ConnectionHolder)、第一次获取到的ConnectionHolder为null
ConnectionHolder conHolder = {
/**
* actualKey:连接池中获取的数据源
* resources:ThreadLocal<Map<Object, Object>>
*/
Map < Object, Object > map = resources.get();
/**
* 第一次肯定为null
*/
if (map == null) {
return null;
}
/**
* ⚠️:这里只是放进去我们配置事务管理器中的数据源
* key:dataSource
* value:contextHolder
*/
return map.get(obtainDataSource());
}
/**
* 事务对象中设置连接ConnectionHolder,ConnectionHolder对象包含conection属性
* ⚠️newConnectionHolde:false
*/
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
//事务是否存在(当前的transaction事务对象是否是激活状态且存在ConnectionHolder那么此处为true)
if (isExistingTransaction(transaction)) {
//⚠️嵌套事务走这里,且直接return
return handleExistingTransaction(def, transaction, debugEnabled);
}
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//true:开启事务成功,false:开启事务失败
//此方法,默认嵌套事务都会开启一个新事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
{
//开启事务,设置autoCommit = false。标记事务状态
doBegin(transaction, definition);
{
/**
* hasConnectionHolder: return(this.connectionHolder != null);
* isSynchronizedWithTransaction:return this.synchronizedWithTransaction;
*/
//判断条件等同:connectionHolder == null || synchronizedWithTransaction
//确保PROPAGATION_REQUIRES_NEW类型的事务嵌套依然会开辟新的连接
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
//⚠️:newConnectionHolde:true
//⚠️:这里为ConnectionHolde开辟了一个新的连接
txObject.setConnectionHolder(new ConnectionHolder(obtainDataSource().getConnection()), true);
}
txObject.getConnectionHolder().setTransactionActive(true);
con = txObject.getConnectionHolder().getConnection();
con.setAutoCommit(false);
}
prepareSynchronization(status, definition);
}
}
}
}
try {
//火炬传递
retVal = invocation.proceedWithInvocation();
} catch (Throwable ex) {
//事务回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
//事务的提交
commitTransactionAfterReturning(txInfo);
}
}