news 2026/4/22 19:42:24

手把手教会你什么是 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/4/17 22:20:05

阿里Java并发编程全优笔记:涵盖并发编程所有核心点!

说到并发编程,很多人第一反应都是:难!难是肯定的,因为并发编程涉及到的知识面太广,你想要学懂并发编程,需要提前储备大量的底层知识,这样学习过程中理解起来才不会那么困难;才能在面…

作者头像 李华
网站建设 2026/4/23 12:48:22

把一套SOLIDWORKS 安装在服务器上,多人共享服务器资源并发设计

在制造业和工程设计领域,SOLIDWORKS作为主流三维CAD软件,其服务器部署与多人并发设计的高效协同已成为企业数字化转型的关键环节。将SOLIDWORKS安装在服务器上实现多人共享并发设计,需从硬件配置、资源管理、软件部署、网络优化、权限管控及安…

作者头像 李华
网站建设 2026/4/23 13:09:22

国科大《ACS Omega》突破:1秒颠覆传统烧结!焦耳热冲击创制“双峰”晶粒SiC,晶粒尺寸激增60%

导语 对于材料研究者而言,数小时乃至数天的管式炉烧结是家常便饭。如何打破这一效率瓶颈,并在此过程中精准调控材料的本征结构,是陶瓷制备领域的前沿挑战。聚合物衍生陶瓷技术虽前景广阔,却长期受制于传统热解工艺的缓慢动力学。 …

作者头像 李华
网站建设 2026/4/23 13:31:42

留学生简历怎么写?2026年分步指南与专属优化技巧

引言:精准狙击招聘官,你的简历差一份“留学专属优化” “海归”背景是优势,却也常在简历环节变成“甜蜜的负担”。回国求职,你的简历不仅要跨越地理距离,更要跨越招聘官的认知差异与筛选系统的技术门槛。一份优秀的简历…

作者头像 李华
网站建设 2026/4/23 13:31:40

计算机Java毕设实战-基于SpringBoot的蔬菜种植管理系统设计与实现精细化管控【完整源码+LW+部署说明+演示视频,全bao一条龙等】

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

作者头像 李华