spring事务源码执行过程分析

本文深入解析Spring框架中事务管理的核心机制,包括动态代理的生成、事务拦截器的执行流程及异常回滚的具体逻辑。

上篇博客解析了事务生成动态代理对象的底层代码,简单总结的话就是一句话:如果添加了事务注解@Transactional,且方法是public的,spring就会给该bean生成代理对象,至于是jdk还是cglib,就看自己的事务方法对应的类是否实现了接口
接下来我们来看生成代理对象之后的执行过程

拦截器执行

org.springframework.transaction.interceptor.TransactionInterceptor

我在学习的时候,用的是cglib代理,因为我没有实现接口,所以使用的是CGLIB代理,在目标方法被调用的的时候,是会调用到

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept; 然后会调用到
	org.springframework.transaction.interceptor.TransactionInterceptor#invoke
	 org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

这个方法我删减了一部分,区分了声明式事务和编程式事务,这里我们只关注声明式事务

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
		final InvocationCallback invocation) throws Throwable {

	// If the transaction attribute is null, the method is non-transactional.
	TransactionAttributeSource tas = getTransactionAttributeSource();
	//获取事务属性
	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
	//获取事务管理器
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

	//下面是声明式事务的处理逻辑
	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
		//看是否有必要创建一个事务,根据事务的传播行为做判断
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
		Object retVal = null;
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			//执行目标方法
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// target invocation exception
			//如果有异常就回滚事务,回滚时,根据配置的rollbackFor进行判断
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			//清除事务信息
			cleanupTransactionInfo(txInfo);
		}
		//提交事务
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}
}

这个方法中,

  • 根据事务注解对应的传播机制,判断是否要创建新的事务,还是嵌套在当前事务中运行等;
  • 接着执行目标方法
  • 在执行完目标方法之后,如果未发生异常,则提交事务
  • 如果发生异常,根据配置的rollbackFor和noRollbackFor等判断是否要回滚,如果无需回滚,就提交事务,反之,则回滚事务

因此,我们着重关注两个方法

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
和
completeTransactionAfterThrowing(txInfo, ex);

传播机制源码

传播机制的源码我整理之后再补充

异常回滚源码

如果在执行目标方法的时候,如果发生异常,捕获到之后,会进入到这个方法来处理

/**
 * 这里是判断是否需要回滚的逻辑
 * 如果在事务注解上指定了回滚的异常类型、或者指定了不回滚的异常类型,就会在这里进行判断
 * 1.如果判断满足回滚的条件,就事务回滚
 * 		先判断开发人员指定的类型,如果业务代码抛出的异常符合指定的类型,就回滚
 * 		如果没有指定,就判断是否是错误(error)或者运行时异常(runTimeException),如果是,就回滚
 *    所以:如果程序员没有指定回滚的异常,默认情况下,如果是运行时异常或者是error(错误),也是会进行事务的回滚
 * 2.否则,就提交事务
 * @param txInfo
 * @param ex
 */
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);
		}
		/**
		 * 如果程序员有指定回滚或者不回滚的异常,就会进入
		 * 	org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn(java.lang.Throwable)进行判断
		 *
		 * 如果没有指定,就默认调用org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(java.lang.Throwable)
		 * 进行判断  
		 */
		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 {
      // 这里else就是不符合回滚的条件,会进行事务的提交(即使发生了异常场景)
			// We don't roll back on this exception.
			// Will still roll back if TransactionStatus.isRollbackOnly() is true.
			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;
			}
		}
	}
}

这里比较重要的一个方法,就是进行异常的判断,判断当前业务代码抛出的异常,是否符合回滚的要求;

在这里插入图片描述

这里这个方法就是来判断当前抛出的异常是否符合在@Transactional注解上配置的异常回滚信息

org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn
 
  @Override
	public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}
		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;
  	// 这里的rollbackRules是在解析@Transactional注解的时候,保存的;parseTransactionAnnotation在这个方法中解析的注解;这里的rollbackRules既有rollbackException中配置的,也有noRollbackException中配置的
		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				/**
				 * depth:当前rule和ex的相似度
				 *
				 * deepest:和ex最相近的depth
				 * winner:相似度最近的RollbackRuleAttribute
				 * 如果当前rule,返回的depth比上一次返回的depth小,且大于0,就用当前这次异常
				 *
				 * 这里会保存和业务代码抛出异常最相似的rule
				 * 比如:
				 * 	我在代码中,抛出了一个java.lang.ArithmeticException: / by zero
				 *
				 * 	如果我在@Transactional(rollbackFor = {Exception.class,ArithmeticException.class})
				 * 	那肯定会返回ArithmeticException这个rule,因为ArithmeticException返回的depth是0
				 * 	Exception返回的的depth是2  下面解释为什么一个是0,一个是2
				 */
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}

		// User superclass behavior (rollback on unchecked) if no rule matches.
    // 如果没有匹配到最相似的异常、或者没有配置回滚异常类,就会执行这里,这里是调用父类的方法进行判断
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}
		/**
		 * 如果相似度最近的rule不是无需回滚的类型,就可以进行事务回滚
		 */
		return !(winner instanceof NoRollbackRuleAttribute);
	}
	// 这里就是父类对应的判断逻辑,也就是说:如果程序员单单加了一个@Transactional注解,那么在业务代码抛出运行时异常后者error的时候,还是会回滚的
	@Override
	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

那我们接着来看上面判断异常相似度的方法:int depth = rule.getDepth(ex);

这里我称之为相似度,不知道是否合适;这里是根据业务代码中抛出的异常A对应的name和程序员指定的异常B对应的name进行比较,如果匹配上,就返回depth,如果匹配不上,就递归调用,用业务代码中抛出的A异常对应的父类和B异常的name进行比较,依次递归调用

/**
	 * 这里其实就是用业务代码抛出的异常和程序员在@Transactional注解中执行的异常信息进行对比
	 *
	 * 实际底层使用的是String.contains()方法
	 * exceptionName:就是程序员指定的异常对应的类名;比如:我指定的是rollbackFor = Exception.class,那这里的exceptionName就是:java.lang.Exception
	 *
	 * 1.如果当前抛出的异常和程序员指定的异常匹配不上,就依次递归调用抛出异常的父类和程序员指定的异常进行比较,
	 * 		1.1 直到匹配上,就返回当前的depth,depth每递归调用一次,就+1
	 * 		1.2	或者是到Throwable依旧没有比对上,这时,就表示我指定的异常和代码抛出的异常不匹配
	 *
	 * 	这两种场景也好验证:
	 * 		1.首先,我在业务代码中,加上这么一行代码:int i = 10/0;
	 * 		2.然后在@Transactional注解中加上rollbackException = Exception.class	或者是rollbackException = IoException.class
	 * 	    这两种异常,最后事务都会回滚,但是效果却是不一样的
	 * 	    如果我加的是rollbackException = Exception.class,这里会匹配上,返回的是depth是2
	 * 	    但是如果加的是rollbackException = IoException.class,这里返回的是-1
	 *
	 * 	    因为:如果是rollbackException = Exception.class;那这里在匹配的时候,会递归调用两次,
	 * 	    int i = 10/0;会抛出java.lang.ArithmeticException: / by zero
	 * 	    ArithmeticException的父类是RuntimeException;RuntimeException的父类是Exception;所以只有递归调用两次,才能匹配到我指定的Exception.class
	 *
	 * 	    但是,如果我指定的是IoException.class,那永远也匹配不上,因为IOException和ArithmeticException都继承了RuntimeException,是并行的关系,在最后
	 * 	    递归调用到父类Throwable的时候,就会返回-1(即使这里返回了-1,最后事务还是会回滚,为什么?因为在方法之后会判断,如果程序员指定的异常和当前业务代码抛出的异常不相似,那就会判断业务代码抛出的异常是否是运行时异常或者error)
	 *
	 * @param exceptionClass:当前业务代码抛出的异常/或者是抛出异常对应的父类
	 * @param depth:相似度/或者说是深度
	 * @return
	 */
	private int getDepth(Class<?> exceptionClass, int depth) {
		if (exceptionClass.getName().contains(this.exceptionName)) {
			// Found it!
			return depth;
		}
		// If we've gone as far as we can go and haven't found it...
		if (exceptionClass == Throwable.class) {
			return -1;
		}
		return getDepth(exceptionClass.getSuperclass(), depth + 1);
	}

所以,在业务代码抛出异常的时候,

  1. 会判断异常的类型和程序员指定的异常类型,会找到匹配度最为相似的异常,最后再判断最相似的异常 是否是noRollbackException配置的异常类,如果是,就无需回滚,提交事务即可
  2. 如果没有匹配上,就判断抛出的异常是否是Error或者RuntimeException,如果是,也会回滚
### Spring 框架事务管理源码详解 #### 1. 基本概念 Spring事务管理机制基于 AOP(面向切面编程)。它通过代理模式实现了声明式事务管理和程序化事务管理。在声明式事务中,`@Transactional` 注解是最常见的形式之一。 事务的核心特性包括 ACID 属性[^1]: - **Atomicity (原子性)**:事务中的所有操作要么全部完成,要么全部不完成。 - **Consistency (一致性)**:事务执行前后数据库状态保持一致。 - **Isolation (隔离性)**:并发事务之间互不影响。 - **Durability (持久性)**:一旦事务提交,其结果永久保存。 此外,Spring 提供了多种事务传播行为和隔离级别设置选项[^1]。 --- #### 2. Spring 事务执行过程 ##### 2.1 事务 AOP 实现原理 Spring 使用 `ProxyFactoryBean` 或 CGLIB 动态代理技术来实现事务拦截功能。当方法被调用时,实际会触发事务拦截器 `TransactionInterceptor` 中的方法逻辑。 以下是核心流程概述: - 方法调用前,判断当前是否存在事务上下文。 - 如果不存在,则尝试创建新事务;如果存在,则根据传播行为决定如何处理现有事务。 - 执行目标方法并捕获异常情况。 - 根据执行结果决定提交还是回滚事务。 ##### 2.2 创建事务过程 在 `createTransactionIfNecessary()` 方法中完成了事务的实际创建工作。此方法主要依赖于底层平台事务管理器(PlatformTransactionManager)接口的具体实现类。 对于 JDBC 数据库访问场景,默认使用的是 `DataSourceTransactionManager` 类型实例作为事务管理者。它的职责包括开启连接、标记只读标志以及注册同步回调函数等操作[^1]。 ```java // TransactionSynchronizationManager 是线程绑定工具类 TransactionSynchronizationManager.initSynchronization(); ``` ##### 2.3 清理线程本地存储信息 每当一个事务结束之后都需要清理掉与之关联的各种资源对象以便释放内存空间。这部分由 `doCleanupAfterCompletion()` 完成[^1]: ```java if (synchronizationsActive && !isExistingTransaction) { TransactionSynchronizationUtils.triggerBeforeCommit(syncList, readOnly); } ``` ##### 2.4 提交事务 正常情况下,在业务逻辑成功完成后将调用 commit() 来正式确认更改内容已写入磁盘文件系统或者远程服务端点之中去。 ```java try { transaction.commit(status); // 调用具体实现者的commit方法 } catch (RuntimeException ex) { throw new TransactionSystemException( "Could not roll back JPA transaction", ex); } ``` ##### 2.5 处理异常 如果发生未被捕获的运行期错误则自动触发 rollback 流程以撤销之前所做的修改动作从而保护数据完整性不受破坏。 --- #### 3. 配置方式对比 ###### XML 方式的配置 传统项目可能仍然采用 XML 文件定义 Bean 和事务规则的方式来进行初始化加载等工作流控制活动[^2]: ```xml <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut expression="execution(* com.example.service.*Service.*(..))" id="serviceOperation"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/> </aop:config> ``` 同时还需要引入 context 名字空间及其对应的 XSD 地址链接地址才能正常使用某些高级特性和扩展插件等功能模块[^4]. ###### Java Config 的方式 现代开发更倾向于利用纯注解的方式来简化繁琐复杂的 XML 编辑任务量,并且能够获得更好的 IDE 支持效果体验感更强一些[^2]: ```java @Configuration @EnableTransactionManagement public class AppConfig { @Bean public DataSource dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); return builder.setType(EmbeddedDatabaseType.HSQL).build(); } @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } } ``` --- #### 4. 特殊场景下的事务管理 当应用程序涉及到跨多个不同类型的资源(如关系型数据库 + NoSQL 存储引擎组合方案设计架构下),单纯依靠单一的数据源无法满足需求时就需要借助分布式全局协调者角色——即 JTA 技术标准协议栈解决方案[^3] : ```xml <bean id="jtaTxMgr" class="org.springframework.transaction.jta.JtaTransactionManager"/> <!-- Or using properties --> @Bean public JtaTransactionManager jtaTransactionManager(){ UserTransaction userTransaction = ...; TransactionManager tm = ...; JtaTransactionManager manager = new JtaTransactionManager(userTransaction,tm); return manager ; } ``` 这种做法虽然强大但也增加了复杂度因此仅适用于特定场合才考虑实施部署上线运营维护管理工作当中去实践探索前行道路方向图景蓝图构想规划构思创意灵感源泉动力支撑保障体系结构框架模型理论依据参考资料文献出处来源背景介绍说明解释阐述论证分析研究探讨交流分享学习进步成长发展提升优化改进完善健全成熟稳定可靠安全高效便捷快速及时准确无误正确有效果显著成果明显优势突出特点鲜明特色独具风格独树一帜独一无
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值