事务提交后执行的代码必须通过transactionsynchronizationmanager.registersynchronization注册到当前事务同步器,否则会立即执行;该方法仅在存在活跃事务时生效,需在@transactional方法内、提交前调用,且注册对象应继承transactionsynchronizationadapter并重写aftercommit()。

事务提交后执行的代码,必须注册到当前事务同步管理器中,否则会立即执行、不等事务提交。
TransactionSynchronizationManager.registerSynchronization 用法
这是最直接、最轻量的方式,适用于单次事务内临时注册后置逻辑。它不依赖 Spring Bean 生命周期,也不需要继承类或定义组件,适合在 service 方法体内动态使用。
-
TransactionSynchronizationManager.registerSynchronization()只在存在活跃事务时才生效;若当前无事务(比如方法没加@Transactional或传播行为为NOT_SUPPORTED),注册会被忽略,afterCommit()永远不会调用 - 必须在事务开启后、提交前注册,常见位置是
@Transactional方法内部,且不能在异步线程或新事务分支里调用(子事务不共享父事务的同步器) - 注册对象必须实现
TransactionSynchronization接口,推荐直接匿名继承TransactionSynchronizationAdapter,只重写afterCommit()即可
示例:
@Transactional
public void updateUserAndNotify(Long userId) {
userRepository.updateStatus(userId, "processed");
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
redisTemplate.delete("user_cache_" + userId);
rabbitTemplate.convertAndSend("topic.exchange", "user.updated", userId);
}
}
);
}
为什么 afterCommit 不执行?常见失效场景
注册了但什么都没发生,基本是以下某一种情况导致的:
- 当前线程没有绑定事务:方法没加
@Transactional,或用了Propagation.NOT_SUPPORTED/Propagation.NEVER - 注册发生在事务已提交之后:比如写在 try-catch 的 finally 块里,或放在事务方法 return 之后(JVM 不会执行)
- 跨线程调用:在新线程(如
new Thread()或@Async)里调用registerSynchronization,该线程没有事务上下文 - 事务被标记为 rollback-only:比如抛出未被捕获的
RuntimeException,此时afterCommit()不会触发,只有afterCompletion(int status)会被调用,且status == STATUS_ROLLED_BACK
afterCommit vs afterCompletion 的选择
二者用途不同,别混用:
-
afterCommit():仅当事务**成功提交**后触发。适合发消息、删缓存、写审计日志等「只在成功时做」的操作 -
afterCompletion(int status):无论提交还是回滚都会触发,通过参数区分状态。适合清理资源、记录失败原因、更新任务表状态等「总得收个尾」的场景
注意:afterCompletion() 中不能访问数据库(连接可能已关闭),也不能再发起新事务操作;而 afterCommit() 内部仍可安全使用 JdbcTemplate 或 EntityManager,只要不开启新事务。
事务嵌套时的同步器行为
Spring 默认使用 PROPAGATION_REQUIRED,外层事务存在时,内层方法不会开启新事务,而是加入外层事务。此时:
- 所有
registerSynchronization()调用都注册到**同一个事务同步器**中 - 只有外层事务真正提交时,所有注册的
afterCommit()才会批量执行 - 如果内层方法抛异常但被外层 catch 住,外层事务仍可能提交,此时所有同步器仍会执行 —— 所以不要假设“内层失败=同步器不执行”
真正需要隔离行为时,应显式指定 PROPAGATION_REQUIRES_NEW,但要注意:新事务的同步器与外层无关,且新事务提交后立即触发其 afterCommit(),不受外层控制。
事务后置处理的关键不在“怎么写”,而在“什么时候注册”和“注册到哪个上下文”。一旦脱离当前事务线程绑定,就彻底失去保障。别把它当成普通回调来用。










