SpringBoot 的事务传播机制与回滚实现

一、事务传播机制(Propagation)

1.Spring 定义了7中事务传播机制

目录

一、事务传播机制(Propagation)

1.Spring 定义了7中事务传播机制

二、 实现类和调用流程

1. 拦截器: TransactionInterceptor

2. 事务管理器:PlatformTransactionManager

3. 事务逻辑执行:TransactionAspectSupport#invokeWithinTransaction

三、异常回滚实现原理

1. 回滚条件判断

2. 回滚规则

3. 回滚核心逻辑(DataSourceTransactionManager)

四、事务传播机制调用图解(REQUIRES_NEW)

五、代码栗子

六、回顾

传播机制调用:

异常回滚:

事务嵌套:


 spring版本说明: 6.2.7

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) 配置.
传播行为类型说明
REQUIRED (默认)存在事务则加入,不存在则创建新事务
REQUIRES_NEW始终创建新事务,挂起当前事务(若存在)
SUPPORTS存在事务则加入,不存在则以非事务运行
MANDATORY必须存在事务,否则抛出IllegalTransactionStateException
NOT_SUPPORTED以非事务方式执行,挂起当前事务(若存在)
NEVER必须在非事务环境下执行,否则抛出异常
NESTED嵌套事务(基于保存点),外层回滚会导致内层回滚,内层回滚不影响外层

二、 实现类和调研流程

1. 拦截器: TransactionInterceptor

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable{
   
}

 主要方法:  

TransactionInterceptor#invoke
	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

2. 事务管理器:PlatformTransactionManager

public interface PlatformTransactionManager extends TransactionManager {

	
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	
	
	void commit(TransactionStatus status) throws TransactionException;

	
	void rollback(TransactionStatus status) throws TransactionException;

}

3. 事务逻辑执行:TransactionAspectSupport#invokeWithinTransaction

	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 TransactionManager tm = determineTransactionManager(txAttr, targetClass);

		if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager rtm) {
			boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
			boolean hasSuspendingFlowReturnType = isSuspendingFunction &&
					COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());

			ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
				Class<?> reactiveType =
						(isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
				ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
				if (adapter == null) {
					throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type [" +
							method.getReturnType() + "] with specified transaction manager: " + tm);
				}
				return new ReactiveTransactionSupport(adapter);
			});

			return txSupport.invokeWithinTransaction(method, targetClass, invocation, txAttr, rtm);
		}

		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager cpptm)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

			Object retVal;
			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
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}

			if (retVal != null && txAttr != null) {
				TransactionStatus status = txInfo.getTransactionStatus();
				if (status != null) {
					if (retVal instanceof Future<?> future && future.isDone()) {
						try {
							future.get();
						}
						catch (ExecutionException ex) {
							Throwable cause = ex.getCause();
							Assert.state(cause != null, "Cause must not be null");
							if (txAttr.rollbackOn(cause)) {
								status.setRollbackOnly();
							}
						}
						catch (InterruptedException ex) {
							Thread.currentThread().interrupt();
						}
					}
					else if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
						// Set rollback-only in case of Vavr failure matching our rollback rules...
						retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
					}
				}
			}

			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			Object result;
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			try {
				result = cpptm.execute(txAttr, status -> {
					TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
					try {
						Object retVal = invocation.proceedWithInvocation();
						if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
							// Set rollback-only in case of Vavr failure matching our rollback rules...
							retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
						}
						return retVal;
					}
					catch (Throwable ex) {
						if (txAttr.rollbackOn(ex)) {
							// A RuntimeException: will lead to a rollback.
							if (ex instanceof RuntimeException runtimeException) {
								throw runtimeException;
							}
							else {
								throw new ThrowableHolderException(ex);
							}
						}
						else {
							// A normal return value: will lead to a commit.
							throwableHolder.throwable = ex;
							return null;
						}
					}
					finally {
						cleanupTransactionInfo(txInfo);
					}
				});
			}
			catch (ThrowableHolderException ex) {
				throw ex.getCause();
			}
			catch (TransactionSystemException ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
					ex2.initApplicationException(throwableHolder.throwable);
				}
				throw ex2;
			}
			catch (Throwable ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
				}
				throw ex2;
			}

			// Check result state: It might indicate a Throwable to rethrow.
			if (throwableHolder.throwable != null) {
				throw throwableHolder.throwable;
			}
			return result;
		}
	}

三、异常回滚实现原理

1. 回滚条件判断

TransactionAspectSupport#completeTransactionAfterThrowing

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 {
				// 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;
				}
			}
		}
	}

2. 回滚规则

  • 默认规则:回滚RuntimeExceptionError

  •   DefaultTransactionAttribute#rollbackOn

  • @Override
    	public boolean rollbackOn(Throwable ex) {
    		return (ex instanceof RuntimeException || ex instanceof Error);
    	}

  • 自定义规则:通过@Transactional注解配置:

  • @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
        public boolean executeCompensation(String transactionId, String errorMessage) {
    }

3. 回滚核心逻辑(DataSourceTransactionManager)

DataSourceTransactionManager#doRollback
    protected void doRollback(DefaultTransactionStatus status) {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject)status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        if (status.isDebug()) {
            this.logger.debug("Rolling back JDBC transaction on Connection [" + String.valueOf(con) + "]");
        }

        try {
            con.rollback();
        } catch (SQLException ex) {
            throw this.translateException("JDBC rollback", ex);
        }
    }

四、事务传播机制图解(REQUIRES_NEW

Spring 事务传播机制流程
序列图

五、代码栗子

@Service
public class OrderService {
    @Autowired
    private UserService userService;

    // 外层事务(默认REQUIRED)
    @Transactional(rollbackFor = Exception.class)
    public void placeOrder(Order order) {
        saveOrder(order);       // 保存订单
        try {
            userService.updateUser(order.getUserId()); // 调用内层事务
        } catch (Exception e) {
            // 处理异常(内层事务回滚不影响外层)
        }
    }
}

@Service
public class UserService {
    // 内层事务(始终新事务)
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUser(Long userId) {
        updateUserStats(userId); // 更新用户统计
        if (someErrorCondition) {
            throw new BusinessException("业务异常"); // 触发回滚
        }
    }
}

六、回顾

  1. 传播机制调用

    • 通过TransactionInterceptor拦截@Transactional方法

    • PlatformTransactionManager根据传播行为管理事务边界

  2. 异常回滚

    • 默认回滚RuntimeExceptionError

    • 可通过rollbackFor/noRollbackFor自定义

    • 最终由事务管理器调用JDBC/JPA的rollback()

  3. 事务嵌套

    • REQUIRES_NEW会挂起当前事务并创建独立事务

    • NESTED使用保存点实现部分回滚

重要提示:事务生效需满足:

  1. 方法必须是public

  2. 调用必须来自代理对象(类内部调用不生效)

  3. 异常需抛出到事务拦截器(被catch的异常不会触发回滚)

  4. 但对于跨库调用的事务来说,那就要使用分布式事务了,如Seata(AT,TCC), RocketMQ 本地消息表机制来实现了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nextera-void

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值