Spring @TransactionalEventListener 解读

前言

我们在业务开发中,强调低耦合编码,典型的比如业务逻辑处理后要进行短信、邮件等通知,对于这类非主要业务逻辑,就需要想办法解耦出去:

java 复制代码
    public void handler() {
        
        ....
        
        // 业务逻辑
        
        ...
        
        
        // 解耦逻辑 如消息、短息、邮件 等
        ....
    }

解耦方式多种多样,常见的如观察者模式(事件监听),在特定的业务动作发生之后,将其包装成事件(event)发送出去,观察者们监听到对应的事件后,便可进行处理。

更重要的是,这种模式真正的做到了 开闭原则,即 对扩展开放、对修改封闭。比如说,当我们对该事件有新的处理逻辑时,只需要新增一个观察者,在新增的观察者中实现其逻辑即可。

可以看到,对原来的业务流程无修改,仅新增一个观察者即可解决。

回归正题

@TransactionalEventListener 是什么?

Spring 提供了事件监听机制(事件类型 ApplicationEvent),通过 @EventListener 可以监听发送的事件消息并处理。

当然,正如其名,@TransactionalEventListener 也仍然是事件监听器,不同的是它与 Spring 的 事务 相关联。

业务逻辑处理:

java 复制代码
    @Transactional(rollbackFor = Exception.class)
    public void handler() {
        
        ....
        
        // 业务逻辑
        
        ...
        
        
        // 解耦逻辑 如消息、短息、邮件 等
        // push event
        ....
    }

监听器:

java 复制代码
    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void eventListenr(ContextRefreshedEvent contextRefreshedEvent) {
    
         // do listener logic

    }

如上,当事件消息已经从业务处理层投递出去后,业务逻辑发生了异常,导致事件回滚。如果是 @EventListener 则仍然会继续处理监听逻辑,很显然发生异常回滚之后不应该继续执行。

这就是 @TransactionalEventListener 可以 确保 Spring 事务提交之后再执行事件监听逻辑....

原理

@TransactionalEventListener 是 Spring 框架中的一个注解,用于在事务提交或回滚时处理事件。它结合了事务管理和事件驱动编程的优点,允许开发者在事务的不同阶段执行事件处理逻辑。

作用

@TransactionalEventListener 的主要作用是监听事务事件,并在事务完成(提交或回滚)后执行相应的事件处理逻辑。它可以确保事件处理逻辑只在事务成功提交后执行,或者在事务回滚时执行。

值得注意的是,如果你的业务层并没有使用 Spring 事务,则 @TransactionalEventListener 监听不起作用,如何让其起作用?

指定注解参数 fallbackExecution=true,即无事务的场景下也执行事件

工作机制

@TransactionalEventListener 依赖于 Spring 的事件发布机制事务管理机制。它通过监听事务的生命周期事件(如提交或回滚),在事务完成后触发相应的事件处理方法。其工作原理如下:

  1. 事件发布:在事务中发布事件。
  2. 事件监听:@TransactionalEventListener 注解的方法会监听该事件。
  3. 事务完成后执行:根据配置,事件处理逻辑会在事务提交成功后或回滚时执行。

1. 注解定义:

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionalEventListener {
    TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
    boolean fallbackExecution() default false;
    String condition() default "";
}
  • phase:指定事件处理的事务阶段。
  • fallbackExecution:如果没有事务活动,是否仍然执行。
  • condition:条件表达式,满足条件时才执行。

2. 事件发布

在事务中发布事件通常使用 ApplicationEventPublisher:

java 复制代码
@Component
public class MyService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Transactional
    public void performAction() {
        
        ...
        // biz logic 
        ...
        
        // push event
        eventPublisher.publishEvent(new MyEvent(this, "事件消息"));
    }
}

3. 事件监听器的注册

Spring 在启动时会扫描所有的 @TransactionalEventListener 注解的方法,并将它们注册为事件监听器。这个过程由 EventListenerMethodProcessor 类负责。

4. 事务事件监听器的处理

TransactionalEventListener 的核心处理逻辑在 ApplicationListenerMethodTransactionalAdapter 类中。这个类实现了 ApplicationListener 接口,并在事务完成后调用相应的事件处理方法。

关键步骤:
  • 事件拦截:当事件发布时,TransactionalApplicationListenerMethodAdapter 会拦截事件。
  • 事务同步:它使用 TransactionSynchronizationManager 注册一个 TransactionSynchronization,以便在事务完成时执行。
  • 事务阶段执行:根据 phase 属性,选择在事务提交、回滚或完成时执行事件处理方法。

5. 事务同步机制

Spring 的事务管理通过 TransactionSynchronizationManager 来管理事务同步。TransactionalApplicationListenerMethodAdapter 会在事务同步中注册一个回调,以便在事务完成时调用事件处理方法。

6. 源码呈现

java 复制代码
class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerMethodAdapter {
    private final TransactionalEventListener annotation;

    public ApplicationListenerMethodTransactionalAdapter(String beanName, Class<?> targetClass, Method method) {
        super(beanName, targetClass, method);
        TransactionalEventListener ann = (TransactionalEventListener)AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class);
        if (ann == null) {
            throw new IllegalStateException("No TransactionalEventListener annotation found on method: " + method);
        } else {
            this.annotation = ann;
        }
    }

    public void onApplicationEvent(ApplicationEvent event) {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
        
            // 监听事件发布,并注册至事务同步器中,当事务处理之后会进行回调
            TransactionSynchronization transactionSynchronization = this.createTransactionSynchronization(event);
            TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);
        } else if (this.annotation.fallbackExecution()) {
            if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && this.logger.isWarnEnabled()) {
                this.logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");
            }

            this.processEvent(event);
        } else if (this.logger.isDebugEnabled()) {
            this.logger.debug("No transaction is active - skipping " + event);
        }

    }

    private TransactionSynchronization createTransactionSynchronization(ApplicationEvent event) {
        return new TransactionSynchronizationEventAdapter(this, event, this.annotation.phase());
    }

    private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter {
        private final ApplicationListenerMethodAdapter listener;
        private final ApplicationEvent event;
        private final TransactionPhase phase;

        public TransactionSynchronizationEventAdapter(ApplicationListenerMethodAdapter listener, ApplicationEvent event, TransactionPhase phase) {
            this.listener = listener;
            this.event = event;
            this.phase = phase;
        }

        public int getOrder() {
            return this.listener.getOrder();
        }

        public void beforeCommit(boolean readOnly) {
            if (this.phase == TransactionPhase.BEFORE_COMMIT) {
                this.processEvent();
            }

        }

        // 监听事务的不同阶段,按指定的阶段触发  事件执行
        public void afterCompletion(int status) {
            if (this.phase == TransactionPhase.AFTER_COMMIT && status == 0) {
                this.processEvent();
            } else if (this.phase == TransactionPhase.AFTER_ROLLBACK && status == 1) {
                this.processEvent();
            } else if (this.phase == TransactionPhase.AFTER_COMPLETION) {
                this.processEvent();
            }

        }

        protected void processEvent() {
            this.listener.processEvent(this.event);
        }
    }
}

核心工作流程小结

  1. 事件发布:在事务中发布事件。
  2. 事件拦截:ApplicationListenerMethodTransactionalAdapter 拦截事件,并检查事务状态。
  3. 注册同步:如果事务活动,注册一个事务同步对象。
  4. 事务阶段执行:在事务的指定阶段(如提交或回滚)调用事件处理方法。

应用

使用场景

  • 确保数据一致性:在事务成功提交后执行某些操作,以确保数据的一致性。例如,发送通知邮件或更新缓存。
  • 延迟执行操作:在事务完成后执行一些非关键的操作,以减少事务的复杂性和执行时间。
  • 事务回滚处理:在事务回滚时执行补偿逻辑或清理操作。

使用技巧

  • 指定阶段:可以通过 phase 属性指定事件处理的阶段,如 AFTER_COMMIT、AFTER_ROLLBACK、AFTER_COMPLETION 等。
  • 条件执行:可以使用 condition 属性来指定事件处理的条件。
  • 异步执行:结合 @Async 注解,可以在事务完成后异步执行事件处理逻辑。

案例:

java 复制代码
import org.springframework.context.event.EventListener;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.stereotype.Component;

@Component
public class MyEventListener {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleEventAfterCommit(MyEvent event) {
        // 事务提交后执行的逻辑
        System.out.println("事务提交后处理事件: " + event.getMessage());
    }

    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleEventAfterRollback(MyEvent event) {
        // 事务回滚后执行的逻辑
        System.out.println("事务回滚后处理事件: " + event.getMessage());
    }
}
相关推荐
程序员cxuan14 小时前
Loop 还没玩明白,Graph Engineering 又火了。
人工智能·后端·程序员
uzong15 小时前
把一面面试总结做成一个 Skill:重复的事,就别每次都重写
后端·面试
霸道流氓气质15 小时前
SpringBoot中基于 AES-GCM + KMS 密钥管理的数据加解密 Starter 实践
java·数据库·spring boot
程序员海军15 小时前
一个 AI 应用开发程序员的一天,都在屏幕前忙些什么?
前端·后端·程序员
乐观的Terry15 小时前
8、发布系统-完整流水线的核心
java·spring boot·spring·spring cloud
不能放弃治疗15 小时前
MCP
后端
万亿少女的梦16816 小时前
基于Spring Boot的游戏交易管理系统设计与实现
java·spring boot·mysql·系统设计·交易管理
江湖十年16 小时前
Go 语言中 YAML to JSON 踩坑笔记
后端·go·json
你驴我17 小时前
WhatsApp 消息撤回与编辑的幂等性设计实践
java·服务器·前端·后端·python
一缕清烟在人间17 小时前
HarmonyOS开发实战:小分享-TextEditPage文字编辑器——Header+TextArea+工具栏
后端·华为·harmonyos·鸿蒙