Spring 中管理事务回调

2026-07-22 13:51:25  阅读 21 次 评论 0 条

TransactionSynchronizationManager.registerSynchronization 是 Spring 框架中用于 动态注册事务生命周期回调钩子(Hook)的静态核心方法。

它允许开发者在当前线程的事务执行到特定阶段(如:提交前提交后回滚后)时,自动触发自定义的业务逻辑。

最常用于事务提交后发送 MQ 消息、清理缓存或发送邮件,以确保这些外围操作只有在数据库事务成功后才执行。


假设在注册用户成功(数据库插入成功)后,需要发送一条 MQ 消息或者是给用户发送邮件。如果直接写在 @Transactional 方法内,一旦后续代码抛出异常导致事务回滚,MQ 消息或邮件却已经发出去了,就会导致数据不一致。

关键生命周期钩子(常用方法)

1、beforeCommit(boolean readOnly): 在事务提交前触发,此时仍在当前事务内。

2、beforeCompletion(): 在事务提交或回滚前触发,用于做最后的资源清理。

3、afterCommit(): (最常用) 事务成功提交后触发,适合做写缓存、发短信邮件等不可逆的外围操作

4、afterCompletion(int status): 事务完成后触发(无论提交还是回滚均会调用)。可以通过入参判断状态

注意事项

1、必须在现有的事务范围内调用

    如果在没有 @Transactional 或没有开启事务的线程中直接调用 registerSynchronization,系统就会抛出 IllegalStateException: Transaction synchronization is not active 异常

    解决:调用前使用 TransactionSynchronizationManager.isSynchronizationActive() 先判断一下。

2、afterCommit 中不能再执行数据库写操作

    当执行到 afterCommit() 时,底层的数据库连接一般是已经完成提交并可能被释放或关闭了。如果在执行带 @Transactional 的数据库保存方法,会引发异常,或者报错连接已关闭

3、需要注意线程隔离

    TransactionSynchronizationManager 的底层基于 ThreadLocal 实现。回调逻辑必须与开启事务的代码在同一个线程中运行。如果在这个事务方法中,开了一个新线程,则在子线程内调用 TransactionSynchronizationManager.registerSynchronization 是不会被主线程触发的。

4、afterCommit 阻塞会拖垮数据库连接池

    虽然 afterCommit 是在提交之后执行,但在整个回调彻底执行完之前,当前线程持有的数据库连接是不会被释放掉的。如果回调逻辑里面有特别耗时间的 RPC 请求、HTTP同步调用、甚至是死循环,这样会导致数据库的连接池被迅速耗尽,会导致怎样的结果不言而喻。

    解决:直接把这个回调扔进自定义线程池异步处理就行


代码示例(AI生成)

方案一:兼容方案(基于硬编码 TransactionSynchronization)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
public class OrderService {

    private static final Logger log = LoggerFactory.getLogger(OrderService.class);

    @Transactional(rollbackFor = Exception.class)
    public void createOrderWithSync(Order order) {
        // 1. 执行核心数据库操作
        orderDao.save(order);
        log.info("【业务】订单落库成功,订单号: {}", order.getId());

        // 2. 安全校验:必须确保当前存在激活的 Spring 事务环境,否则注册会抛出 IllegalStateException
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            
            // 3. 动态注册事务同步回调
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

                @Override
                public void beforeCommit(boolean readOnly) {
                    // 触发时机:数据库连接的 commit() 即将调用前。此时仍在当前事务内。
                    // 避坑:可以进行最后的校验或数据准备,但不要在这里做耗时过长的操作。
                    log.info("【钩子-beforeCommit】事务准备提交,是否只读事务: {}", readOnly);
                }

                @Override
                public void afterCommit() {
                    // 触发时机:数据库物理 commit 成功之后。
                    // 典型场景:发送 MQ 消息、清除 Redis 缓存、发送通知短信。
                    // 避坑:此时数据库连接尚未归还连接池,严禁在此处执行耗时超长的 RPC 或 HTTP 同步调用!
                    log.info("【钩子-afterCommit】事务已成功提交!开始执行外围非事务逻辑(如发送 MQ,更新缓存)");
                    
                    // 建议:耗时任务应异步化,防止阻塞数据库连接归还
                    // executor.execute(() -> mqService.sendOrderMessage(order));
                }

                @Override
                public void afterCompletion(int status) {
                    // 触发时机:整个事务链路彻底结束(无论成功提交还是回滚都会执行)。
                    // 常用作:ThreadLocal 的清理、临时文件的删除、监控埋点。
                    if (status == TransactionSynchronization.STATUS_COMMITTED) {
                        log.info("【钩子-afterCompletion】事务流程最终状态:[成功提交]");
                    } else if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
                        // 典型场景:在这里触发业务层面的补偿机制,或者记录异常回滚日志
                        log.warn("【钩子-afterCompletion】事务流程最终状态:[发生回滚],开始清理临时数据...");
                    }
                }
            });
        } else {
            // 如果没有事务环境,则直接走兜底的普通同步/异步业务逻辑
            log.warn("【警告】当前无激活事务环境,直接触发外围逻辑");
        }
    }
}

方案二:Spring 4.2+ 方案(基于 Spring Event + 声明式注解)

此方案推荐用于 Spring 4.2+ 之后的项目。它通过应用事件(ApplicationEvent)解耦业务骨架与外围钩子,代码中完全看不到侵入式的事务管理器代码。


1.、定义事件载体(POJO 类)

public class OrderCreatedEvent {
    private final Order order;

    public OrderCreatedEvent(Order order) {
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }
}

2、核心业务类(发布事件)

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ModernOrderService {

    private final ApplicationEventPublisher eventPublisher;
    private final OrderDao orderDao;

    public ModernOrderService(ApplicationEventPublisher eventPublisher, OrderDao orderDao) {
        this.eventPublisher = eventPublisher;
        this.orderDao = orderDao;
    }

    @Transactional(rollbackFor = Exception.class)
    public void createOrder(Order order) {
        // 1. 核心数据库操作
        orderDao.save(order);
        
        // 2. 只负责发布事件,不再关心事务何时提交,彻底实现业务解耦
        // 注意:事件发布本身是同步的,但监听器的执行时机受控制
        eventPublisher.publishEvent(new OrderCreatedEvent(order));
    }
}

3. 事件监听类(声明式钩子)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Component
public class OrderTransactionListener {

    private static final Logger log = LoggerFactory.getLogger(OrderTransactionListener.class);

    /**
     * 场景一:对应 beforeCommit
     */
    @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
    public void onBeforeCommit(OrderCreatedEvent event) {
        log.info("【BEFORE_COMMIT】收到事件,事务即将提交。订单号: {}", event.getOrder().getId());
    }

    /**
     * 场景二:对应 afterCommit (如果不写 phase 属性,默认就是 AFTER_COMMIT)
     */
    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void onAfterCommit(OrderCreatedEvent event) {
        log.info("【AFTER_COMMIT】数据库事务已确认提交!安全处理外围逻辑");
    }
    @Async
    @TransactionalEventListener(
        phase = TransactionPhase.AFTER_COMMIT, 
        fallbackExecution = true // 如果当前没有事务,立刻执行该逻辑(兜底)
    )
    public void onAfterCommit(OrderCreatedEvent event) {
        log.info("【AFTER_COMMIT】执行外围逻辑");
    }

    /**
     * 场景三:对应 afterCompletion 中的回滚状态
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void onAfterRollback(OrderCreatedEvent event) {
        log.warn("【AFTER_ROLLBACK】捕获到数据库事务回滚!执行异常补偿机制...");
    }

    /**
     * 场景四:对应 afterCompletion (无论提交或回滚均触发)
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
    public void onAfterCompletion(OrderCreatedEvent event) {
        log.info("【AFTER_COMPLETION】事务链路结束,进行日志收尾。");
    }
}



微信扫码查看本文
本文地址:https://www.yangguangdream.com/?id=2274
版权声明:本文为原创文章,版权归 编辑君 所有,欢迎分享本文,转载请保留出处!
NEXT:已经是最新一篇了

发表评论


表情

还没有留言,还不快点抢沙发?