Spring在事务提交之后 执行异步方法
2025-03-31 本文已影响0人
饱饱抓住了灵感
在 Spring 中,若要让异步方法在事务提交后执行,可以通过 事务同步机制(TransactionSynchronization) 结合 异步执行 实现。
一、核心思路
-
事务内注册回调:在事务方法中,通过
TransactionSynchronizationManager注册一个事务提交后的回调。 -
异步触发逻辑:在回调的
afterCommit()方法中调用异步方法,确保异步逻辑在事务提交后执行。
二、实现步骤
1. 开启事务和异步支持
在配置类中启用事务和异步功能:
@Configuration
@EnableTransactionManagement
@EnableAsync
public class AppConfig {
// 配置线程池(可选,默认使用 SimpleAsyncTaskExecutor)
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.initialize();
return executor;
}
}
2. 定义异步方法
在 Service 中定义异步方法,使用 @Async 注解:
@Service
public class AsyncService {
@Async("taskExecutor") // 指定线程池
public void asyncMethodAfterCommit(String data) {
System.out.println("异步执行,线程:" + Thread.currentThread().getName());
// 异步业务逻辑
}
}
3. 在事务中注册回调
在事务方法中,通过 TransactionSynchronizationAdapter 注册事务提交后的回调:
@Service
public class TransactionalService {
@Autowired
private AsyncService asyncService;
@Transactional
public void transactionalMethod() {
// 业务逻辑...
System.out.println("事务内操作,线程:" + Thread.currentThread().getName());
// 注册事务提交后的回调
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 事务提交后执行异步方法
asyncService.asyncMethodAfterCommit("data");
}
}
);
}
}
三、关键点说明
-
事务与异步的协作:
- 直接调用
asyncMethodAfterCommit()会立即异步执行,但无法保证事务已提交。 - 通过
TransactionSynchronizationAdapter.afterCommit(),确保异步逻辑仅在事务成功提交后触发。
- 直接调用
-
事务传播行为影响:
- 如果事务是
REQUIRED(默认传播行为),异步方法会在原事务提交后执行。 - 若事务回滚,
afterCommit()不会触发,异步方法也不会执行。
- 如果事务是
-
线程安全:
- 异步方法需避免共享可变状态,或通过同步机制保证线程安全。
四、afterCommit的核心规则
执行顺序
-
事务提交时机:
事务的提交操作发生在事务方法返回之前(由 Spring AOP 代理确保)。 -
回调触发时机:
afterCommit()回调会在事务提交成功后触发,但仍在事务方法返回的同一线程中执行。 -
代码执行顺序:
事务方法内部的代码 → 事务提交 → 回调执行 → 事务方法返回 → 调用方后续逻辑。
示例
@Service
public class TestService {
@Transactional
public void transactionalMethod() {
System.out.println("事务内操作");
// 注册回调
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
System.out.println("回调执行:事务已提交");
}
}
);
System.out.println("事务方法即将返回");
}
}
@Service
public class InvokeService{
@Resource
private TestService testService;
public void callerMethod() {
testService.transactionalMethod(); // 调用事务方法
System.out.println("调用方后续逻辑");
}
}
输出结果:
事务内操作
事务方法即将返回
回调执行:事务已提交
调用方后续逻辑
结论
-
事务提交与回调的关系:
- 事务提交发生在事务方法返回之前(由 Spring 代理管理)。
-
afterCommit()回调在事务提交完成后触发,但仍在事务方法返回的线程中同步执行。 - 因此,事务方法返回后,回调会立即执行,随后才是调用方的后续逻辑。
-
线程模型:
- 默认情况下,事务方法和回调在同一线程中执行,确保顺序性。
- 若回调中调用
@Async异步方法,则异步逻辑会转移到其他线程,但afterCommit()本身仍会在原线程中完成。
-
适用场景:需确保回调逻辑在事务提交后执行,但对实时性要求不高的场景(如发送通知、更新缓存)。
五、注意事项
-
依赖注入问题:在
TransactionSynchronization中直接注入 Bean 可能导致空指针(因代理问题)。建议通过ApplicationContextAware获取 Bean:@Service public class TransactionalService implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext context) { this.context = context; } public void transactionalMethod() { // 通过上下文获取 Bean AsyncService asyncService = context.getBean(AsyncService.class); // 注册回调... } } -
事务边界:确保异步方法不依赖事务内的临时数据(如未提交的数据库记录)。