news 2026/10/17 23:04:05

手把手教会你什么是 Spring 事件监听 —— 解耦神器,告别“面条代码”!(Spring Boot 实战)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教会你什么是 Spring 事件监听 —— 解耦神器,告别“面条代码”!(Spring Boot 实战)

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)


一、真实业务痛点:为什么你需要事件监听?

想象你在开发一个用户注册系统,注册成功后要:

  1. 发送欢迎邮件;
  2. 发送短信验证码;
  3. 记录操作日志;
  4. 初始化用户积分;
  5. 推送消息到企业微信……

❌ 反例:全部写在注册方法里

@Service public class UserService { public void register(User user) { // 1. 保存用户 userRepository.save(user); // 2. 发邮件 emailService.sendWelcomeEmail(user.getEmail()); // 3. 发短信 smsService.sendVerificationCode(user.getPhone()); // 4. 记日志 logService.log("用户注册", user.getId()); // 5. 初始化积分 pointService.initPoints(user.getId()); // 6. 推送企业微信 wechatService.pushRegisterMessage(user.getName()); } }

🚨问题爆炸:

  • 方法又长又臭,像“意大利面条”;
  • 每新增一个功能(比如加“发优惠券”),就要改核心注册逻辑;
  • 如果发邮件失败,整个注册就失败?不合理!
  • 单元测试困难,要 mock 所有依赖。

这就是典型的“高耦合”代码!


二、什么是 Spring 事件监听?一句话讲透

Spring 事件监听 = 观察者模式 + Spring 容器管理
它允许你:发布一个事件,所有关心这个事件的组件自动响应,彼此完全解耦!

就像你发朋友圈:

  • 你只管“发布”动态(事件);
  • 好友 A 点赞,好友 B 评论,好友 C 转发(监听器);
  • 你不需要知道谁会响应,他们也不需要知道你是谁!

三、手把手实战:用事件监听重构用户注册

第一步:定义自定义事件

// 继承 ApplicationEvent(Spring 4.2+ 后可省略,但建议保留) public class UserRegisteredEvent extends ApplicationEvent { private final User user; public UserRegisteredEvent(Object source, User user) { super(source); this.user = user; } public User getUser() { return user; } }

💡source通常是发布事件的对象(如当前 Service),可用于追踪来源。


第二步:在注册成功时发布事件

@Service public class UserService { // 注入 Spring 的事件发布器 @Autowired private ApplicationEventPublisher eventPublisher; public void register(User user) { // 1. 保存用户(核心逻辑) userRepository.save(user); // 2. 发布事件!后续操作全部解耦 eventPublisher.publishEvent(new UserRegisteredEvent(this, user)); } }

✅关键:注册方法现在只做“注册”,其他事交给监听器!


第三步:编写多个监听器(每个只做一件事)

📧 邮件监听器
@Component public class EmailListener { @EventListener // ←←← 核心注解! public void handleUserRegistered(UserRegisteredEvent event) { User user = event.getUser(); System.out.println("【邮件服务】发送欢迎邮件给: " + user.getEmail()); // emailService.send(...); } }
📱 短信监听器
@Component public class SmsListener { @EventListener public void handleUserRegistered(UserRegisteredEvent event) { User user = event.getUser(); System.out.println("【短信服务】发送验证码到: " + user.getPhone()); // smsService.send(...); } }
📝 日志监听器
@Component public class LogListener { @EventListener public void handleUserRegistered(UserRegisteredEvent event) { User user = event.getUser(); System.out.println("【日志服务】记录用户注册: " + user.getId()); // logService.log(...); } }

✅优势:

  • 每个监听器职责单一;
  • 新增“发优惠券”?只需写一个新监听器,注册逻辑一行都不用改!

四、高级用法:让事件更强大

1️⃣ 异步监听(避免阻塞主流程)

默认事件是同步的(监听器执行完才返回)。如果发邮件很慢,用户要等很久!

✅ 解决方案:加@Async

@Component public class AsyncEmailListener { @EventListener @Async // ←←← 开启异步(需在启动类加 @EnableAsync) public void handleUserRegistered(UserRegisteredEvent event) { // 模拟耗时操作 try { Thread.sleep(3000); } catch (Exception e) {} System.out.println("【异步邮件】发送完成!"); } }

⚠️ 注意:在Application.java上加@EnableAsync!


2️⃣ 条件监听(只处理特定事件)

@EventListener(condition = "#event.user.vip == true") public void handleVipUserRegistered(UserRegisteredEvent event) { System.out.println("【VIP 专属】发送豪华礼包!"); }

使用 SpEL 表达式,灵活过滤。


3️⃣ 监听 Spring 内置事件

Spring 自己也会发布事件,比如容器启动完成:

@Component public class StartupListener { @EventListener public void onContextRefreshed(ContextRefreshedEvent event) { System.out.println("【系统启动】应用已初始化完毕!"); // 可用于加载缓存、预热数据等 } }

常用内置事件:

事件触发时机
ContextRefreshedEventApplicationContext 初始化或刷新完成
ContextClosedEventApplicationContext 关闭
RequestHandledEventWeb 请求处理完成

五、反例对比:为什么不用 if-else 或直接调用?

方案耦合度扩展性异常处理可测试性
直接调用(反例)高(UserService 依赖所有服务)差(改核心代码)全局回滚难(要 mock 所有)
事件监听(正例)零耦合极好(加监听器即可)独立失败不影响主流程每个监听器单独测

💡特别提醒:
如果某个监听器失败(比如邮件服务挂了),不会影响其他监听器和主流程!
(除非你手动抛异常并配置了事务回滚)


六、完整 Spring Boot 示例

1. 项目结构

src/main/java ├── event/ │ └── UserRegisteredEvent.java ├── listener/ │ ├── EmailListener.java │ ├── SmsListener.java │ └── LogListener.java ├── service/ │ └── UserService.java └── Application.java // 加 @EnableAsync

2. 启动类

@SpringBootApplication @EnableAsync // 开启异步支持 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

3. 测试 Controller

@RestController public class TestController { @Autowired private UserService userService; @PostMapping("/register") public String register(@RequestBody User user) { userService.register(user); return "注册成功!后续操作已异步执行。"; } }

调用:

POST /register { "name": "张三", "email": "zhangsan@example.com", "phone": "13800138000" }

控制台输出:

注册成功!后续操作已异步执行。 【日志服务】记录用户注册: 1 【短信服务】发送验证码到: 13800138000 【异步邮件】发送完成!

七、注意事项 & 最佳实践

⚠️ 1. 事件对象应该是不可变的(Immutable)

  • 避免监听器修改事件数据,导致其他监听器拿到脏数据;
  • 建议用final字段 + 构造函数初始化。

⚠️ 2. 不要在监听器中写核心业务逻辑

  • 事件监听适合非核心、可降级的操作(通知、日志、统计等);
  • 核心逻辑(如扣款、库存)仍应在主流程中处理。

⚠️ 3. 异步监听的异常处理

  • 默认异步异常会被吞掉!建议加全局异常处理器:
@Component public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { System.err.println("异步监听器异常: " + ex.getMessage()); } }

⚠️ 4. 事务边界问题

  • 如果发布事件的方法有@Transactional,监听器默认在同一事务中;
  • 若需监听器在事务提交后执行,用@TransactionalEventListener。

八、总结:事件监听的核心价值

场景传统写法事件监听
用户注册后发邮件UserService 直接调 EmailServiceUserService 发事件,EmailListener 监听
订单支付后更新库存OrderService 调 InventoryServiceOrderService 发事件,InventoryListener 监听
系统启动加载缓存在 main 方法里写监听 ContextRefreshedEvent

✅记住:
“我不关心谁来处理,我只负责通知” —— 这就是事件驱动的魅力!


视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 7:03:53

LOOKUP函数典型用法合集

LOOKUP函数主要用于在查找范围中查询指定的查找值,并返回另一个范围中对应位置的值。 她有两个特点: 1、要求查询区域必须升序进行排序。如果没有经过排序,LOOKUP函数也会认为排在数据区域最后的内容,是该区域中最大的。 2、当…

作者头像 李华
网站建设 2026/10/17 3:20:43

Java程序员如何深入学习Spring源码?

金三银四也快要到了,不知道大家最近面试的时候有没有被问到过Spring相关问题(循环依赖、事务、生命周期、传播特性、IOC、AOP、设计模式、源码)?拿Spring来说,现在面试面试官一般会直接问:谈一下你对Spring…

作者头像 李华
网站建设 2026/10/15 20:52:18

HTTP 请求方法选择与 RESTful 实践(对比 GraphQL、RPC)

HTTP请求方法在实际开发中并非仅使用POST,但确实存在简化使用现象。 早期因技术限制(如浏览器表单仅支持GET/POST)和简化思维导致过度使用POST。 现代开发推荐RESTful风格:GET查询、POST创建、PUT/PATCH更新、DELETE删除&#xff…

作者头像 李华
网站建设 2026/10/16 15:55:04

AI 驱动人才管理落地难?Moka 全流程解决方案助力企业破局

在数字化转型浪潮下,企业对人力资源管理的效率与精准度要求不断提升,智慧人力信息系统逐渐成为企业管理的重要工具。很多 HR 从业者和企业管理者想了解智慧人力信息系统的具体定义与价值,也希望找到实现 AI 驱动全流程人才管理的有效路径。本…

作者头像 李华
网站建设 2026/10/15 4:20:21

便携式移动气象监测设备

便携式移动气象监测设备设计与实现 一、设计背景与意义 气象监测在农业生产、环境治理、科研勘探、应急救援等领域至关重要,传统气象监测设备体积庞大、依赖固定站点、部署成本高,难以满足移动观测与临时监测需求。现有便携气象设备多存在参数测量单一…

作者头像 李华
网站建设 2026/10/17 21:17:12

便携式信号发生器

便携式信号发生器设计与实现 一、设计背景与意义 信号发生器作为电子测量、电路调试、教学实验的核心工具,广泛应用于电子工程、通信技术、科研实验等领域。传统台式信号发生器存在体积庞大、依赖市电、操作复杂等问题,难以满足户外现场调试、移动设备维…

作者头像 李华