news 2026/6/15 12:06:02

手把手教会你什么是 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/6/14 1:30:14

【课程设计/毕业设计】基于java+springboot的海洋航运管理系统设计实现基于SpringBoot的海洋航运管理系统的设计与实现【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/6/11 6:25:44

期刊论文投稿屡被拒?虎贲等考 AI:从选题到录用的学术发表加速器

“改了 8 版的论文,被核心期刊拒稿理由是‘创新点不足’”“文献引用格式混乱,编辑部让返工 3 次”“实证数据缺乏支撑,审稿人直接打回重写”—— 学术发表路上,无数科研人陷入 “选题难、写作慢、录用率低” 的循环。普通 AI 工具…

作者头像 李华
网站建设 2026/6/10 19:32:20

文件与文件夹批量更名工具FileReNameTool V2.1.1支持撤销改名

大家好,我是大飞哥。日常整理文件时,我们总遇到批量文件命名混乱、手动改名效率低下的痛点 —— 比如照片、文档、素材需要按规则重命名,手动逐个修改不仅耗时,还容易出错,而简易工具功能单一,无法满足复杂…

作者头像 李华
网站建设 2026/6/13 5:21:23

邮件系统怎么选?2026年六大主流邮件系统对比

在数字化办公日益普及的今天,企业邮箱早已不再是简单的收发邮件工具,而是成为内部协作、安全管理、品牌形象和客户信任的核心枢纽。面对市场上众多的邮件系统,企业如何根据自身需求做出明智选择?本文将对比分析2026年六款主流邮件…

作者头像 李华