Spring Boot实战:Event事件机制解析与实战
😄生命不息,写作不止
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 博客首页 @怒放吧德德 To记录领地 @一个有梦有戏的人
🌝分享学习心得,欢迎指正,大家一起学习成长!
转发请携带作者信息 @怒放吧德德(掘金) @一个有梦有戏的人(CSDN)

前言
在Spring Boot应用开发中,业务逻辑的紧耦合是代码难以维护和扩展的主要根源。当核心业务与众多拓展逻辑交织在一起时,任何微小的改动都可能牵一发而动全身。Spring事件机制基于经典的观察者模式,为这一痛点提供了优雅的解决方案------它让核心业务只需专注自身,通过发布事件即可"通知"各方,而拓展业务则通过监听事件自主响应,真正实现解耦与隔离。本文将从设计思想到实战应用,由浅入深地解析Spring Boot事件机制的核心原理与进阶技巧,帮助你在实际项目中写出更优雅、更可维护的代码。
本次案例代码仓库:gitee.com/liyongde/ja...
1. 概述
在设计模式体系中,观察者模式是实现业务解耦的经典设计模式,维基百科对其定义为:一个目标对象管理所有依赖的观察者对象,在自身状态改变时主动发出通知,通过调用观察者的方法完成交互,广泛用于实时事件处理系统。
在日常业务开发中,观察者模式的核心价值就是解耦核心业务与拓展业务 。以电商用户下单场景为例,下单成功后通常需要完成发放优惠券、发送短信通知、增加用户积分、记录运营日志等操作,传统写法会将所有逻辑耦合在一个方法中:

plain
// 传统耦合写法
public void placeOrder(Order order) {
// 核心逻辑:创建订单
orderMapper.insert(order);
// 拓展逻辑:一堆业务调用
couponService.sendCoupon(order.getUserId());
smsService.sendNotify(order.getPhone());
pointsService.addPoints(order.getUserId(), order.getAmount());
opsLogService.recordLog(order);
}
这种写法的问题显而易见:新增/删除拓展逻辑必须修改核心方法、某个拓展服务异常会阻塞核心下单流程、拓展逻辑无法单独测试。
使用Spring事件机制(基于观察者模式实现)后,代码会变得异常优雅:

OrderService完成核心的下单逻辑后,只需发布一个OrderCreatedEvent事件,无需关注任何拓展逻辑;其他服务只需订阅该事件,即可实现自定义的业务处理,完美实现核心业务与拓展业务的解耦。
观察者模式 vs 发布订阅模式
很多胖友会混淆这两个模式,简单来说:发布订阅模式是广义上的观察者模式 ,在观察者模式的Subject(目标)和Observer(观察者)之间引入了Event Channel(事件通道)作为中介,进一步解耦发布者和订阅者,而Spring事件机制是标准的观察者模式实现,发布者直接与观察者交互。
2. Spring 事件机制核心三要素
Spring基于JDK内置的事件机制(java.util.EventObject/java.util.EventListener)进行拓展,实现了轻量级、易使用的事件机制,核心由事件、事件发布者、事件监听器三部分组成,三者各司其职、协同工作。
2.1 事件(ApplicationEvent)
- 所有自定义事件的基类 ,继承自JDK的
java.util.EventObject - 内置
source属性(获取事件源,即发布事件的对象)和timestamp属性(获取事件发生时间) - 自定义事件只需继承该类,添加业务所需的成员变量和getter方法即可
2.2 事件发布者(ApplicationEventPublisher)
- 负责发布事件 的核心接口,提供
publishEvent()方法发布事件 - 注入方式:实现
ApplicationEventPublisherAware接口(推荐)或直接@Autowired注入 - Spring容器本身就是一个
ApplicationEventPublisher实现类,可直接在容器中使用
2.3 事件监听器(ApplicationListener / @EventListener)
- 负责监听并处理指定事件 ,有两种实现方式:
- 实现
ApplicationListener接口(继承自JDK的java.util.EventListener),通过泛型指定监听的事件类型 - 在方法上添加
@EventListener注解(Spring 4.2+推荐),无需实现接口,更简洁灵活
- 实现
- 监听器会自动感知容器中的事件,当发布对应类型的事件时,会触发监听器的处理方法
3. 入门实战示例
本次实战以电商用户下单 为业务场景,实现核心下单逻辑与拓展逻辑的解耦,技术栈为Spring Boot 2.7.10 + Spring MVC,实现下单成功后发放优惠券、发送短信通知、增加用户积分三个拓展功能。(结构关系如图)

3.1 引入依赖
创建Spring Boot项目,在pom.xml中引入核心依赖,只需引入spring-boot-starter-web即可(内置Spring事件机制相关依赖):
plain
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-event-demo</artifactId>
<dependencies>
<!-- Spring Web 核心依赖,提供容器和接口能力 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 日志依赖,方便打印执行日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
</project>
3.2 自定义事件:OrderCreatedEvent
创建下单成功事件类,继承ApplicationEvent,封装下单后的核心业务数据(订单ID、用户ID、手机号、订单金额),作为事件传递的载体:
java
/**
* 下单成功事件
*/
public class OrderCreatedEvent extends ApplicationEvent {
/**
* 订单ID
*/
private Long orderId;
/**
* 用户ID
*/
private Long userId;
/**
* 用户手机号
*/
private String phone;
/**
* 订单金额
*/
private BigDecimal amount;
// 构造方法必须传入source(事件源)
public OrderCreatedEvent(Object source, Long orderId, Long userId, String phone, BigDecimal amount) {
super(source);
this.orderId = orderId;
this.userId = userId;
this.phone = phone;
this.amount = amount;
}
// ...
}
3.3 事件发布者:OrderService
创建订单服务,实现ApplicationEventPublisherAware接口,注入事件发布器,在核心下单逻辑完成后发布下单成功事件:
java
/**
* 订单服务(事件发布者)
*/
@Service
public class OrderService implements ApplicationEventPublisherAware {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
// 事件发布器
private ApplicationEventPublisher applicationEventPublisher;
// 注入事件发布器
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* 核心下单方法
* @param userId 用户ID
* @param phone 手机号
* @param amount 订单金额
* @return 订单ID
*/
public Long placeOrder(Long userId, String phone, BigDecimal amount) {
// 1. 核心业务逻辑:生成订单(模拟,实际为数据库插入)
Long orderId = System.currentTimeMillis(); // 用时间戳模拟订单ID
logger.info("[placeOrder][核心逻辑] 下单成功,订单ID:{},用户ID:{}", orderId, userId);
// 2. 发布下单成功事件
applicationEventPublisher.publishEvent(
new OrderCreatedEvent(this, orderId, userId, phone, amount)
);
return orderId;
}
}
3.4 事件监听器:实现拓展业务
实现三个拓展业务的监听器,分别使用接口实现 和注解 两种方式,演示Spring事件监听器的灵活用法,并为短信服务添加异步执行(非核心逻辑不阻塞主线程)。
3.4.1 短信服务:实现ApplicationListener接口(异步)
plain
package cn.sourcecode.event.service;
import cn.sourcecode.event.event.OrderCreatedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* 短信服务(事件监听器:接口实现方式)
*/
@Service
public class SmsService implements ApplicationListener<OrderCreatedEvent> {
private static final Logger logger = LoggerFactory.getLogger(SmsService.class);
// 异步执行:添加@Async注解,避免短信发送阻塞下单流程
@Override
@Async
public void onApplicationEvent(OrderCreatedEvent event) {
logger.info("[onApplicationEvent][短信服务] 给用户{}({})发送下单通知,订单ID:{}",
event.getUserId(), event.getPhone(), event.getOrderId());
// 实际业务:调用短信接口发送通知
}
}
3.4.2 优惠券服务:@EventListener注解方式(指定执行顺序)
java
@Slf4j
@Service
public class CouponService implements Ordered {
// 监听下单成功事件
@EventListener
public void sendCoupon(OrderCreatedEvent event) {
log.info("[sendCoupon][优惠券服务] 给用户{}发放满减优惠券,订单金额:{}",
event.getUserId(), event.getAmount());
// 实际业务:发放优惠券并写入数据库
}
@Override
public int getOrder() {
// 指定执行顺序为1(优先级最高)
return 1;
}
}
3.4.3 积分服务:@EventListener注解方式
java
@Slf4j
@Service
public class PointsService implements Ordered {
// 监听下单成功事件
@EventListener
public void addPoints(OrderCreatedEvent event) {
// 模拟:订单金额1元=1积分
int points = event.getAmount().intValue();
log.info("[addPoints][积分服务] 给用户{}增加{}积分,订单ID:{}",
event.getUserId(), points, event.getOrderId());
// 实际业务:增加用户积分并更新
}
@Override
public int getOrder() {
// 指定执行顺序为2
return 2;
}
}
3.5 控制层:OrderController
创建接口层,提供HTTP接口用于测试下单功能,调用订单服务的核心方法:
java
/**
* 订单控制层
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 下单接口
* @param userId 用户ID
* @param phone 手机号
* @param amount 订单金额
* @return 下单结果
*/
@GetMapping("/place")
public String placeOrder(@RequestParam Long userId,
@RequestParam String phone,
@RequestParam BigDecimal amount) {
Long orderId = orderService.placeOrder(userId, phone, amount);
return "下单成功,订单ID:" + orderId;
}
}
3.6 应用启动类:EventDemoApplication
启动类需添加@EnableAsync注解,开启Spring异步功能 (支持监听器的@Async注解):
plain
/**
* 应用启动类
*/
@SpringBootApplication
@EnableAsync // 开启异步功能,支持@Async注解
public class EventDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EventDemoApplication.class, args);
}
}
3.7 简单测试
执行完毕之后可以看到以下日志
plain
[placeOrder][核心逻辑] 下单成功,订单ID:1719999999999,用户ID:1001
[sendCoupon][优惠券服务] 给用户1001发放满减优惠券,订单金额:99
[addPoints][积分服务] 给用户1001增加99积分,订单ID:1719999999999
[onApplicationEvent][短信服务] 给用户1001(13800138000)发送下单通知,订单ID:1719999999999
日志说明:
- 核心下单逻辑先执行,完成后发布事件
- 优惠券服务(order=1)优先于积分服务(order=2)执行,实现了监听器顺序控制
- 短信服务在独立线程执行,不阻塞主线程的同步逻辑
4. Spring 内置核心事件
Spring框架和Spring Boot在启动、运行、关闭的整个生命周期中,会自动发布大量内置事件,我们可以通过监听这些事件,实现对容器和应用的生命周期拓展,无需自定义事件即可完成个性化需求。
4.1 ApplicationContextEvent:容器生命周期事件

这是Spring IoC容器的核心事件基类,所有容器相关事件都继承自它,对应容器的创建、刷新、启动、停止、关闭等生命周期阶段,核心实现类:
- ContextRefreshedEvent:容器初始化/刷新完成事件(最常用,容器启动完成后触发)
- ContextStartedEvent :容器启动完成事件(调用
start()方法后触发) - ContextStoppedEvent :容器停止完成事件(调用
stop()方法后触发) - ContextClosedEvent :容器关闭开始事件(调用
close()方法后触发)
应用场景 :在ContextRefreshedEvent事件中初始化系统参数、加载本地缓存、连接第三方服务等。
4.2 SpringApplicationEvent:应用生命周期事件

这是Spring Boot应用的核心事件基类,对应应用的启动、准备、运行、失败等生命周期阶段,核心实现类:
- ApplicationStartingEvent:应用启动开始事件(仅SpringApplication.run()方法刚执行时触发)
- ApplicationEnvironmentPreparedEvent:应用环境准备完成事件(配置文件、环境变量加载完成)
- ApplicationContextInitializedEvent :Spring Context 准备完成,但是
Bean Definition未加载时的事件 - ApplicationPreparedEvent:Spring Context 准备完成,但是未刷新时的事件。
- ApplicationReadyEvent:应用启动成功事件(所有Bean初始化完成,可处理请求,最常用)
- ApplicationFailedEvent:应用启动失败事件(Bean初始化、容器启动异常时触发)
应用场景 :在ApplicationReadyEvent中打印应用启动成功日志、记录启动时间;在ApplicationFailedEvent中实现启动失败的告警通知。
4.3 实战常用内置事件
除了生命周期事件,Spring Cloud生态中还有很多实用的内置事件:
- RefreshRoutesEvent:Spring Cloud Gateway网关路由刷新事件,结合Nacos可实现动态路由
- RefreshRemoteApplicationEvent:Spring Cloud Config配置刷新事件,结合RabbitMQ可实现分布式配置动态刷新
- RefreshEvent:Spring Cloud上下文刷新事件,触发配置重新加载
5. 高级拓展技巧
掌握基础用法后,胖友们可以尝试这些高级技巧,让Spring事件机制更贴合复杂的业务场景,以下是实际开发中最常用的4个拓展点,附核心代码示例。
5.1 控制监听器执行顺序
当多个监听器监听同一个事件时,默认执行顺序是不确定的,可通过3种方式指定顺序(数值越小,优先级越高 ),注意:异步监听器(@Async)不支持顺序控制。
- @Order注解 (类级别控制):在监听器类上添加
@Order(1) - 实现Ordered接口 (动态控制):实现
getOrder()方法,支持运行时动态返回顺序值
plain
// 实现Ordered接口示例
@Service
public class PointsService implements Ordered {
@EventListener
public void addPoints(OrderCreatedEvent event) {
// 业务逻辑
}
// 动态指定顺序为2
@Override
public int getOrder() {
return 2;
}
}
5.2 事务绑定事件:@TransactionalEventListener
在实际业务中,核心业务(如下单)通常带有事务,若监听器在事务提交前执行,可能出现数据未持久化导致监听器读取不到数据 的问题。Spring 4.2+提供@TransactionalEventListener注解,可将监听器绑定到事务的指定阶段。
核心属性
- phase :事务阶段,可选值:
TransactionPhase.BEFORE_COMMIT:事务提交前执行TransactionPhase.AFTER_COMMIT(默认):事务提交成功后执行TransactionPhase.AFTER_ROLLBACK:事务回滚后执行TransactionPhase.AFTER_COMPLETION:事务完成后执行(无论提交/回滚)
- fallbackExecution :无事务时是否执行,默认
false(无事务则不执行)
代码示例
java
// 订单服务添加事务
@Transactional
public Long placeOrder(Long userId, String phone, BigDecimal amount) {
// 核心业务逻辑
}
// 监听器绑定到事务提交后执行
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendCoupon(OrderCreatedEvent event) {
// 事务提交成功后,再发放优惠券,避免数据不一致
}
5.3 智能监听器:SmartApplicationListener
若需要一个监听器同时监听多种事件 ,或根据事件源过滤事件 ,可实现SmartApplicationListener接口,替代传统的ApplicationListener接口,实现更灵活的事件监听。
代码示例
java
@Service
public class SmartBizListener implements SmartApplicationListener {
// 指定监听的事件类型(可添加多个)
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return OrderCreatedEvent.class.isAssignableFrom(eventType)
|| UserRegisterEvent.class.isAssignableFrom(eventType);
}
// 指定监听的事件源类型(仅处理指定发布者的事件)
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return OrderService.class.isAssignableFrom(sourceType)
|| UserService.class.isAssignableFrom(sourceType);
}
// 事件处理逻辑
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof OrderCreatedEvent) {
// 处理下单事件
} else if (event instanceof UserRegisterEvent) {
// 处理注册事件
}
}
// 执行顺序
@Override
public int getOrder() {
return 1;
}
}
5.4 自定义事件广播器:ApplicationEventMulticaster
Spring默认使用SimpleApplicationEventMulticaster作为事件广播器,负责将事件推送给所有匹配的监听器。若需要自定义事件广播规则(如自定义线程池、过滤监听器、异步策略),可实现ApplicationEventMulticaster接口,或继承SimpleApplicationEventMulticaster进行拓展。
应用场景:为所有异步监听器指定自定义线程池,替代默认的SimpleAsyncTaskExecutor。
总结
Spring事件机制是基于观察者模式的轻量级解耦方案,无需引入额外中间件,即可实现核心业务与拓展业务的解耦,其核心优势体现在:
- 解耦:发布者无需知道监听器的存在,新增/删除拓展逻辑无需修改核心代码
- 灵活:支持同步/异步执行、事务绑定、顺序控制,适配各种业务场景
- 轻量:基于Spring原生能力,无需引入MQ等中间件,学习成本低
- 可拓展:支持自定义事件、监听器、广播器,贴合复杂业务需求
实际开发建议:
- 核心业务(如下单、注册)保持纯净,只处理核心逻辑,拓展逻辑通过事件实现
- 非核心、耗时的拓展逻辑(如发送短信、邮件、调用第三方接口)使用
@Async异步执行 - 涉及数据库操作的拓展逻辑,使用
@TransactionalEventListener绑定事务,保证数据一致性 - 多个监听器存在依赖关系时,通过
order属性指定执行顺序
Spring事件机制适合单体应用或轻量级分布式应用的解耦,若需要跨服务、跨节点的事件通知,可结合RabbitMQ/Kafka等消息中间件实现,形成「本地事件+分布式事件」的双层解耦方案。
附录
1\]. 文章大致参考芋道(其更为丰富,可以看一下):[芋道 Spring Boot 事件机制 Event 入门 \| 芋道源码 ------ 纯源码解析博客](https://link.juejin.cn?target=https%3A%2F%2Fwww.iocoder.cn%2FSpring-Boot%2FEvent%2F%3Fgithub "https://www.iocoder.cn/Spring-Boot/Event/?github") \[2\]. 代码仓库地址:[Trial-8-Spring-Event-Demo](https://link.juejin.cn?target=https%3A%2F%2Fgitee.com%2Fliyongde%2Fjava-trial%2Ftree%2Fmaster%2FTrial8-Spring-Event%2FTrial-8-Spring-Event-Demo "https://gitee.com/liyongde/java-trial/tree/master/Trial8-Spring-Event/Trial-8-Spring-Event-Demo") *** ** * ** *** *转发请携带作者信息* **@怒放吧德德 @一个有梦有戏的人** 持续创作很不容易,作者将以尽可能的详细把所学知识分享各位开发者,一起进步一起学习。**转载请携带链接,转载到微信公众号请勿选择原创,谢谢!** 👍创作不易,如有错误请指正,感谢观看!记得点赞哦!👍 谢谢支持!