核心内容摘要
知识的传承,生命的延续:当教育遇上生育的梦想
视频看了几百小时还迷糊关注我几分钟让你秒懂发点评论可以给博主加热度哦
真实业务痛点为什么你需要事件监听想象你在开发一个用户注册系统注册成功后要发送欢迎邮件发送短信验证码记录操作日志初始化用户积分推送消息到企业微信……❌ 反例全部写在注册方法里Service public class UserService { public void register(User user) { //
保存用户 userRepository.save(user); //
发邮件 emailService.sendWelcomeEmail(user.getEmail()); //
发短信 smsService.sendVerificationCode(user.getPhone()); //
记日志 logService.log(用户注册, user.getId()); //
初始化积分 pointService.initPoints(user.getId()); //
推送企业微信 wechatService.pushRegisterMessage(user.getName()); } }问题爆炸方法又长又臭像“意大利面条”每新增一个功能比如加“发优惠券”就要改核心注册逻辑如果发邮件失败整个注册就失败不合理单元测试困难要 mock 所有依赖。
这就是典型的“高耦合”代码
什么是 Spring 事件监听一句话讲透Spring 事件监听 观察者模式 Spring 容器管理它允许你发布一个事件所有关心这个事件的组件自动响应彼此完全解耦就像你发朋友圈你只管“发布”动态事件好友 A 点赞好友 B 评论好友 C 转发监听器你不需要知道谁会响应他们也不需要知道你是谁
手把手实战用事件监听重构用户注册第一步定义自定义事件// 继承 ApplicationEventSpring
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) { //
保存用户核心逻辑 userRepository.save(user); //
发布事件后续操作全部解耦 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️⃣ 异步监听避免阻塞主流程默认事件是同步的监听器执行完才返回。
如果发邮件很慢用户要等很久✅ 解决方案加AsyncComponent public class AsyncEmailListener { EventListener Async // ←←← 开启异步需在启动类加 EnableAsync public void handleUserRegistered(UserRegisteredEvent event) { // 模拟耗时操作 try { Thread.sleep(
; } catch (Exception e) {} System.out.println(【异步邮件】发送完成); } }⚠️ 注意在Application.java上加EnableAsync2️⃣ 条件监听只处理特定事件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 示例
项目结构src/main/java ├── event/ │ └── UserRegisteredEvent.java ├── listener/ │ ├── EmailListener.java │ ├── SmsListener.java │ └── LogListener.java ├── service/ │ └── UserService.java └── Application.java // 加 EnableAsync
启动类SpringBootApplication EnableAsync // 开启异步支持 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
测试 ControllerRestController public class TestController { Autowired private UserService userService; PostMapping(/register) public String register(RequestBody User user) { userService.register(user); return 注册成功后续操作已异步执行。
; } }调用POST /register { name: 张三, email: zhangsanexample.com, phone: 13800138000 }控制台输出注册成功后续操作已异步执行。
【日志服务】记录用户注册: 1 【短信服务】发送验证码到: 13800138000 【异步邮件】发送完成
七、
注意事项 最佳实践⚠️
事件对象应该是不可变的Immutable避免监听器修改事件数据导致其他监听器拿到脏数据建议用final字段 构造函数初始化。
⚠️
不要在监听器中写核心业务逻辑事件监听适合非核心、可降级的操作通知、日志、统计等核心逻辑如扣款、库存仍应在主流程中处理。
⚠️
异步监听的异常处理默认异步异常会被吞掉建议加全局异常处理器Component public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler { Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { System.err.println(异步监听器异常: ex.getMessage()); } }⚠️
事务边界问题如果发布事件的方法有Transactional监听器默认在同一事务中若需监听器在事务提交后执行用TransactionalEventListener。
八、