Spring 事件驱动用法总结

一、Spring 事件驱动完整流程总结

1. 定义事件:基类封装 + 业务事件继承

(1)事件基类:TestEvent(继承 Spring 标准 ApplicationEvent

作用:封装通用事件数据,作为所有相关事件的父类,减少重复代码。

核心:继承 ApplicationEvent,通过构造器传递事件源(source) 和通用业务数据(cmd),并提供 getter 供子类 / 监听器获取。

java 复制代码
// 事件基类:封装通用事件数据(可根据业务选择是否需要通用事件封装)
public class TestEvent extends ApplicationEvent {
    @Getter
    private CommonTestData cmd;

    // 构造器:必传事件源 + 通用业务数据
    public TestEvent(Object source, CommonTestData  cmd) {
        super(source); // 调用父类ApplicationEvent构造器,绑定事件源
        this.cmd = cmd;
    }
}

(2)业务事件:RealTimeTestEvent(继承 TestEvent)

作用:基于基类扩展,定义具体业务场景的事件,无需重复封装通用数据。

核心:直接复用父类 TestEvent 的构造器,仅传递业务所需参数,简化事件定义。

java 复制代码
// 业务事件:继承TestEvent,专注业务场景
public class RealTimeTestEvent extends TestEvent {
    // 直接复用父类构造器,传递事件源 + 业务数据cmd
    public RealTimeTestEvent(Object source, CommonTestData cmd) {
        super(source, cmd);
    }
}
2. 发布事件:业务方法中触发

注入 Spring 内置 ApplicationEventPublisher,在核心业务逻辑完成后发布事件。

可通过开关(如 isTestEventTestEnable)控制是否发布,灵活适配业务需求。

java 复制代码
@Autowired
private ApplicationEventPublisher eventPublisher;

// 业务方法中发布事件(核心逻辑执行后)
if (isTestEventTestEnable) {
    // 构造业务事件,传递事件源(this) + 业务数据md
    eventPublisher.publishEvent(new RealTimeTestEvent(this, md));
}
3. 监听事件:异步监听 + 自定义线程池

监听器类加 @Service(或 @Component),被 Spring 容器管理。

用 @EventListener 绑定具体事件,@Async 指定自定义线程池,实现异步非阻塞处理。

从事件中通过 getCmd() 获取通用业务数据,执行附加逻辑。

java 复制代码
@Slf4j
@Service
public class RealTimeTestEventListener {

    @SneakyThrows
    // 1. 指定自定义线程池,异步执行(不阻塞主线程)
    @Async("RealTimeTestEventThreadPool")
    // 2. 监听RealTimeTestEvent事件(匹配事件类型)
    @EventListener(RealTimeTestEvent.class)
    public void handleEvent(RealTimeTestEvent event) {
        // 从事件基类TestEvent中获取通用业务数据
        CommonTestData md = event.getCmd();
        // 执行业务附加逻辑
        MktDataTestRealTimeListener.pushRealTimeTest(
            ...
        );
    }
}
4. 线程池配置:自定义隔离 + 动态参数

用 @Configuration 声明配置类,@Bean 定义线程池,供 @Async 调用。

从配置文件读取线程池参数(核心数、最大数、队列容量),支持动态调整。

设置线程名前缀,方便日志排查;配置拒绝策略(如 CallerRunsPolicy)避免任务丢失。

java 复制代码
@Configuration
public class TestEventThreadPool {
    @Autowired
    private ThreadPoolProperties threadPoolProperties;

    // 定义线程池Bean,指定名称供@Async调用
    @Bean("realTimeTestEventThreadPool")
    public Executor realTimeTestEventThreadPool() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 动态读取配置参数
        taskExecutor.setCorePoolSize(threadPoolProperties.getRealTimeTest().getCoreSize());
        taskExecutor.setMaxPoolSize(threadPoolProperties.getRealTimeTest().getMaxPoolSize());
        taskExecutor.setQueueCapacity(threadPoolProperties.getRealTimeTest().getQueueCapacity());
        // 通用配置:线程名前缀、拒绝策略等
        taskExecutor.setThreadNamePrefix("realTimeTestEventThreadPool--");
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化线程池
        taskExecutor.initialize();
        return taskExecutor;
    }
}

二、@EventListener vs @TransactionalEventListener:核心区别与适用场景

1. 核心区别(一句话总结)

@EventListener:事件发布即执行,不依赖事务状态;

@TransactionalEventListener:绑定事务阶段执行(如事务提交后),专为事务场景设计。

2.关键事务阶段(phase)

@TransactionalEventListener 支持 4 种事务阶段,按需选择:

TransactionPhase.BEFORE_COMMIT:事务提交前执行(慎用,可能阻塞事务提交);

TransactionPhase.AFTER_COMMIT:事务提交成功后执行(最常用,保证数据一致性);

TransactionPhase.AFTER_ROLLBACK:事务回滚后执行(如回滚补偿逻辑);

TransactionPhase.AFTER_COMPLETION:事务完成后(无论提交 / 回滚)执行。

3. 适用场景选型(结合你的业务)

(1)用 @EventListener 的场景

业务无数据库事务,或事务与事件处理无数据依赖;不操作数据库,无数据一致性风险,用 @EventListener 更简洁高效。

事件处理是轻量操作(如推送消息、记录日志、更新缓存),无需等待事务提交;

(2)用 @TransactionalEventListener 的场景

事件处理依赖主事务的数据库数据(如主事务更新数据后,监听器需读取最新数据);

需避免 "事务未提交,监听器读取脏数据" 的问题;

三、整体核心价值总结

代码复用:通过 TestEvent 基类封装通用事件数据,业务事件仅需继承,减少重复代码;

业务解耦:主业务只负责发布事件,监听器独立处理附加逻辑,新增 / 删除逻辑无需修改主业务;

性能优化:@Async + 自定义线程池,让附加逻辑异步执行,不阻塞主线程,提升接口响应速度;

稳定性保障:自定义线程池控制并发,避免默认线程池的 OOM 风险;@TransactionalEventListener 解决事务场景的数据一致性问题;

灵活扩展:支持多事件、多监听器、多线程池隔离,适配复杂业务场景。

四、快速选型口诀

非事务、轻量处理、无数据依赖 → 用 @EventListener;

事务场景、需读最新事务数据、保证一致性 → 用 @TransactionalEventListener(phase = AFTER_COMMIT)。

五、Spring 事件驱动和消息中间件选型

(1)、Spring 事件驱动:仅适用于同一个服务(进程内) 本地解耦

Spring 事件驱动(ApplicationEvent/@EventListener/@TransactionalEventListener)的底层是基于 Spring 容器的内存事件广播机制,事件的发布、监听全程都在同一个 JVM 进程内完成,无法跨服务、跨进程、跨服务器传递。

核心价值:解决单服务内部的业务解耦问题;

优势:轻量、无中间件依赖、执行效率高、无需考虑网络 / 序列化问题;

局限:仅单服务有效,服务集群部署时,事件仅在当前发布事件的服务实例中触发,其他实例无法感知。
(2)、跨服务解耦:必须使用消息中间件(主流选型)

当需要跨服务、跨进程、跨服务器传递事件 / 消息时,Spring 事件驱动完全无法满足,此时消息中间件(MQ) 是标准且唯一的解决方案,其核心作用是实现分布式系统中的异步通信与解耦。

主流消息中间件选型(按场景适配):

RabbitMQ:基于 AMQP 协议,支持丰富的消息路由(直连、主题、扇出等)、消息确认、死信队列,适配复杂的业务场景(如订单支付后跨服务通知库存、物流),企业级项目首选;

Kafka:基于发布 - 订阅模式,高吞吐、低延迟、高可用,适合大数据量、高并发的日志收集、实时数据同步、流处理场景;

RocketMQ:阿里开源,兼顾 RabbitMQ 的功能丰富性和 Kafka 的高吞吐,支持分布式事务消息,适合电商、金融等核心业务场景;

ActiveMQ:老牌消息中间件,协议支持全面,但性能和高可用略逊于前三者,适合中小型项目或传统项目。

相关推荐
凯子坚持 c2 小时前
C++基于微服务脚手架的视频点播系统---客户端(2)
开发语言·c++·微服务
Beginner x_u2 小时前
JavaScript 中浅拷贝与深拷贝的差异与实现方式整理
开发语言·javascript·浅拷贝·深拷贝
柯一梦2 小时前
STL2--vector的介绍以及使用
开发语言·c++
毕设源码-邱学长2 小时前
【开题答辩全过程】以 基于Springboot个人健康运动系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
愿你天黑有灯下雨有伞2 小时前
Spring Boot + FastExcel:打造完美的导入校验功能
java·spring boot·后端
Rainly20002 小时前
java原生实现企业级spring batch数据迁移
java·spring·batch
云霄IT2 小时前
go语言post请求遭遇403反爬解决tls/ja3指纹或Cloudflare防护
开发语言·后端·golang
自动化控制仿真经验汇总2 小时前
电子抑振控制实验中MATLAB+示波器的用法-PART-RIGOL-电磁制振
开发语言·matlab