Spring管理事务默认回滚的异常是什么?

本文详细解析了Spring事务管理中默认回滚策略的工作原理。当出现RuntimeException或Error时,默认情况下会触发事务回滚;而对于非运行时异常,则不会自动回滚,除非通过rollBackFor属性进行配置。

问题:

Spring管理事务默认(即没有rollBackFor的情况下)可以回滚的异常是什么?

回答:

RuntimeException或者Error。

抛出运行时异常,是否回滚?Yes

@Transactional
public boolean rollbackOn(Throwable ex) {
	return new RuntimeException();
}
抛出错误,是否回滚?Yes
@Transactional
public void testTransationB(){
	throw new Error();
}
抛出非运行时异常,是否回滚?No
@Transactional
public void testTransationC() throws Exception{
	throw new Exception();
}
抛出非运行时异常,有rollBackFor=Exception.class的情况下,是否回滚?Yes
@Transactional(rollbackFor = Exception.class)
public void testTransationD() throws Exception{
	throw new Exception();
}

分析:

请看Spring源码类RuleBasedTransactionAttribute:

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;

	if (this.rollbackRules != null) {
		for (RollbackRuleAttribute rule : this.rollbackRules) {
			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);
	}
		
	return !(winner instanceof NoRollbackRuleAttribute);
}

其中

ex:程序运行中抛出的异常,可以是Exception,RuntimeException,Error;

rollbackRules:代表rollBackFor指定的异常。默认情况下是empty。

所以默认情况下,winner等于null,程序调用super.rollbackOn(ex)

Here is the source code of super.rollbackOn(ex)

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

真相大白,结论是,默认情况下,如果是RuntimeException或Error的话,就返回True,表示要回滚,否则返回False,表示不回滚。

有rollBackFor的情况就不分析啦,这个也是可以触类旁通的。



博文引用自:http://blog.youkuaiyun.com/u012557814/article/details/50685374

抛出错误,是否回滚?Yes
抛出非运行时异常,有rollBackFor=Exception.class的情况下,是否回滚?Yes
<think>我们正在讨论异步任务中的事务回滚异常隔离问题。根据用户之前的引用和问题,我将围绕以下核心点展开: 1. **事务边界问题**:异步方法通常会在新线程中执行,导致事务上下文无法跨线程传播(默认情况下),因此需要特殊处理事务管理[^2]。 2. **异常处理机制**:异步任务中的异常不会自动回滚主线程事务,需要显式捕获并处理[^3][^4]。 3. **解决方案**:包括使用编程式事务管理、线程间事务上下文传递、以及合理的异常处理策略。 --- ### 一、异步事务的核心挑战 1. **事务上下文隔离** 默认情况下,Spring事务管理(`@Transactional`)基于 `ThreadLocal` 存储事务状态。当任务切换到新线程时,事务上下文将**丢失**,导致: - 异步方法内无法参与外层事务 - 异步方法内异常不会触发外层事务回滚[^2] 2. **异常传播中断** 异步方法内抛出的异常会被封装到 `Future` 或 `CompletableFuture` 中,主线程**无法直接捕获**: ```java @Async public void asyncTask() { // 此处的异常不会传播到调用方 throw new RuntimeException("Async error"); } ``` --- ### 二、解决方案:事务回滚异常隔离 #### 方案1:编程式事务管理(手动控制事务边界) 在异步方法内部使用 `TransactionTemplate` 或 `PlatformTransactionManager` 手动管理事务: ```java @Autowired private TransactionTemplate transactionTemplate; @Async public void asyncTaskWithTransaction() { transactionTemplate.execute(status -> { try { // 业务逻辑... return result; } catch (Exception e) { status.setRollbackOnly(); // 手动标记回滚 throw e; } }); } ``` **优点**:事务范围精确控制 **缺点**:代码侵入性强[^1] #### 方案2:委托给事务性方法 将核心业务逻辑抽离到单独的 Spring Bean 中,并对其添加 `@Transactional` 注解: ```java @Service public class CoreService { @Transactional // 事务在此生效 public void transactionalOperation() { // 业务逻辑... } } @Service public class AsyncService { @Autowired private CoreService coreService; @Async public void asyncTask() { coreService.transactionalOperation(); // 调用事务方法 } } ``` **原理**:通过 Spring 代理机制确保事务生效 **注意**:需确保 `CoreService` 是通过 Spring 注入的代理对象[^2] #### 方案3:上下文传递 + 事务同步 使用 `TransactionSynchronizationManager` 和 `TaskDecorator` 传递事务资源: ```java @Bean public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setTaskDecorator(new ContextPropagatingDecorator()); return executor; } public class ContextPropagatingDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { // 捕获主线程事务上下文 Map<Object, Object> resources = TransactionSynchronizationManager.getResourceMap(); return () -> { try { // 绑定到异步线程 resources.forEach((key, value) -> TransactionSynchronizationManager.bindResource(key, value) ); runnable.run(); } finally { // 清理资源 resources.keySet().forEach(TransactionSynchronizationManager::unbindResource); } }; } } ``` **适用场景**:需要跨线程共享数据库连接等事务资源(高风险操作,需严格测试)[^2] --- ### 三、异常隔离处理策略 1. **异步方法内捕获并处理异常** 避免异常传播到线程池导致线程终止: ```java @Async public CompletableFuture<Void> safeAsyncTask() { try { // 业务代码 return CompletableFuture.completedFuture(null); } catch (Exception e) { // 记录日志/触发补偿 return CompletableFuture.failedFuture(e); } } ``` 2. **全局异步异常处理器** 实现 `AsyncUncaughtExceptionHandler` 统一处理: ```java @Configuration public class AsyncConfig implements AsyncConfigurer { @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -> { // 报警或回滚逻辑 System.err.println("Async method error: " + method.getName()); }; } } ``` 3. **通过 `Future` 获取异常结果** 主线程主动检查异步任务状态: ```java Future<?> future = asyncService.asyncTask(); try { future.get(); // 显式等待结果(阻塞) } catch (ExecutionException e) { // 处理子线程异常 transactionManager.rollback(); // 主线程事务手动回滚(如果需要) } ``` --- ### 四、最佳实践总结 | **场景** | **推荐方案** | |------------------------------|---------------------------------------| | 简单异步任务 | 方案2(委托事务方法)+ 全局异常处理器 | | 需要精细控制事务边界 | 方案1(编程式事务) | | 跨线程共享事务资源(高级) | 方案3(上下文传递) | | 主线程依赖异步结果 | 方案3 + `Future.get()` 异常处理 | > **关键原则**: > - 异步方法的事务应**自包含**,避免跨线程事务传播 > - 主线程事务和异步线程事务**相互独立**(设计上解耦) > - 通过消息队列等中间件实现最终一致性(替代分布式事务) --- §§相关问题§§ 1. 如何在 Spring Boot 中配置全局异步异常处理器? 2. `@TransactionalEventListener` 如何处理异步事务事件? 3. 异步任务中如何实现跨服务的分布式事务? 4. 使用 `TransactionTemplate` 时如何设置事务传播行为? 5. 如何避免异步任务阻塞主线程事务提交? </think>### 异步任务中事务回滚异常隔离的解决方案 #### 一、核心挑战 1. **事务上下文丢失** 异步任务在新线程执行,Spring 默认的 `ThreadLocal` 事务上下文不会跨线程传播 ```java @Async public void processOrder(Order order) { // 此处无事务上下文,@Transactional 注解失效 } ``` 2. **异常传播中断** 异步方法内的异常无法自动触发主线程事务回滚[^2]: ```java @Transactional public void createOrder(Order order) { orderRepo.save(order); // 主线程事务 asyncService.processOrder(order); // 子线程异常不会回滚事务 } ``` #### 二、事务回滚解决方案 ##### 方案1:编程式事务(推荐) 在异步方法内部手动管理事务: ```java @Autowired private PlatformTransactionManager transactionManager; @Async public void asyncWithTransaction() { TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { // 业务操作 transactionManager.commit(status); } catch (Exception ex) { transactionManager.rollback(status); // 显式回滚[^1] throw ex; // 抛出异常供全局处理器捕获 } } ``` ##### 方案2:嵌套事务方法 创建独立事务方法并通过代理调用: ```java @Service public class TransactionalService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void executeInTransaction() { // 业务逻辑 } } @Async public void asyncTask() { transactionalService.executeInTransaction(); // 开启新事务 } ``` #### 三、异常隔离策略 ##### 1. 全局异常处理器 实现 `AsyncUncaughtExceptionHandler` 统一处理: ```java @Configuration public class AsyncConfig implements AsyncConfigurer { @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -> { // 1. 记录错误日志 // 2. 发送报警通知 // 3. 写入异常队列[^3] System.err.println("异步方法 " + method.getName() + " 异常: " + ex.getMessage()); }; } } ``` ##### 2. Future 结果捕获 通过 `CompletableFuture` 获取异步异常: ```java @Async public CompletableFuture<Void> asyncTask() { return CompletableFuture.runAsync(() -> { if(condition) throw new BusinessException(); }); } // 调用方处理 public void mainMethod() { CompletableFuture<Void> future = service.asyncTask(); future.exceptionally(ex -> { System.out.println("捕获到异步异常: " + ex.getMessage()); return null; }); } ``` #### 四、关键实践建议 1. **事务边界设计** - 保持异步任务事务自包含(单一职责) - 避免跨线程事务传播(分布式事务替代方案) 2. **异常处理规范** ```mermaid graph LR A[异步任务开始] --> B{是否关键业务} B -- 是 --> C[记录异常+触发补偿] B -- 否 --> D[记录日志+标记失败状态] C --> E[通知人工干预] D --> F[自动重试机制] ``` 3. **线程池隔离策略** ```java @Bean(name = "criticalTaskExecutor") public Executor criticalExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix("CRITICAL-"); // 线程池隔离 return executor; } @Async("criticalTaskExecutor") // 指定执行器 public void criticalAsyncTask() { ... } ``` #### 五、典型场景解决方案 **支付订单异步处理:** ```java @Async @Transactional(propagation = Propagation.NOT_SUPPORTED) // 显式挂起事务 public void processPaymentAsync(Long orderId) { TransactionTemplate template = new TransactionTemplate(transactionManager); template.execute(status -> { try { Payment payment = paymentService.create(orderId); auditService.log(payment); // 独立事务 return payment; } catch (Exception ex) { status.setRollbackOnly(); // 当前事务回滚 compensationService.scheduleCompensation(orderId); // 启动补偿 throw ex; } }); } ``` > **最佳实践**: > - 关键业务:采用编程式事务 + 补偿机制 > - 普通业务:使用 `@Transactional(propagation=REQUIRES_NEW)` > - 耗时操作:需配置单独线程池避免资源耗尽[^4] ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值