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());
    }
}
相关推荐
long31610 分钟前
java 策略模式 demo
java·开发语言·后端·spring·设计模式
rannn_1111 小时前
【Javaweb学习|黑马笔记|Day1】初识,入门网页,HTML-CSS|常见的标签和样式|标题排版和样式、正文排版和样式
css·后端·学习·html·javaweb
柏油1 小时前
Spring @Cacheable 解读
redis·后端·spring
小小工匠3 小时前
Maven - Spring Boot 项目打包本地 jar 的 3 种方法
spring boot·maven·jar·system scope
两码事4 小时前
告别繁琐的飞书表格API调用,让飞书表格操作像操作Java对象一样简单!
java·后端
shark_chili4 小时前
面试官再问synchronized底层原理,这样回答让他眼前一亮!
后端
灵魂猎手5 小时前
2. MyBatis 参数处理机制:从 execute 方法到参数流转全解析
java·后端·源码
易元5 小时前
模式组合应用-桥接模式(一)
后端·设计模式
柑木5 小时前
隐私计算-SecretFlow/SCQL-SCQL的两种部署模式
后端·安全·数据分析