设计模式—观察者模式(超全工程面试+MQ关联详解)

发布时间:2026/7/8 5:37:42
设计模式—观察者模式(超全工程面试+MQ关联详解) 本文目录如下1. 为什么需要观察者模式1.1 观察者模式解决的核心痛点1.2 典型使用场景1.3 不适合观察者模式的场景2. 观察者模式核心体系与角色2.1 核心特点2.2 四大标准角色3. 观察者模式标准实战实现推模型·工程最常用3.1 业务场景3.2 抽象观察者3.3 具体观察者多业务联动3.4 抽象主题 具体主题3.5 客户端调用4 观察者模式标准实战实现拉模型4.1 拉模型业务场景4.2 拉模型核心代码实现4.3 拉模型客户端测试5. 推模型 vs 拉模型核心区别面试必背5.1 推模型5.2 拉模型6. 观察者模式优缺点6.1 优点6.2 缺点针对于上述案例7. 企业级成熟实现SpringBoot异步事件监听生产落地版7.1 业务场景7.2 企业级落地优势对比原生手写7.3 业务场景6.4 第一步定义订单事件事件主体-承载数据7.5 第二步定义多个观察者事件监听器7.6 第三步事件发布器主题Subject7.7 第四步开启异步启动类注解7.8 执行结果 核心特性证明8、企业级进阶事务绑定事件解决生产最大BUG9. 企业版 VS 手写基础版核心对比10. 工程常见问题与解决方案10.1 同步阻塞导致主接口RT高10.2 事件丢失、异常雪崩10.3 内存泄漏10.4 执行顺序不可控11. 重点拓展观察者模式 与 消息队列MQ深度对比11.1 核心关系MQ本质就是「分布式观察者模式」11.2 详细区别工程落地必背11.3 落地选型规范企业级标准12. 高频面试易混模式区分12.1 观察者模式 vs 策略模式12.2 观察者模式 vs 中介者模式13. 主流框架源码落地面试高频13.1 JDK 原生Observer13.2 Spring事件机制经典落地13.3 注册器、监控、配置中心14. 核心总结1. 为什么需要观察者模式当业务存在「一个行为触发多个后续动作」的联动场景且后续需要频繁新增、删除联动业务时使用观察者模式替代硬编码链式调用。1.1 观察者模式解决的核心痛点a. 业务联动硬编码耦合严重主流程完成后挨个调用后续业务方法主流程依赖所有子业务代码臃肿。b. 新增联动业务需要修改主流程代码每加一个后续动作就要改动主方法严重违反开闭原则。c. 主流程阻塞严重、响应变慢所有后续同步逻辑串行执行主接口RT持续拉高。d. 联动逻辑无法复用、无法独立管控订阅逻辑写死在业务内部无法动态注册、动态注销。e. 事件分发与事件处理混为一谈发布逻辑、消费逻辑耦合无法单独拓展、降级、异步。1.2 典型使用场景a. 一对多业务联动场景订单创建后触发发短信、发推送、记录日志、更新库存、积分发放。b. 状态变更自动通知场景配置变更、热点数据更新、缓存刷新、状态流转推送。c. 事件驱动型业务场景消息监听、事件回调、流程钩子、业务后置处理。d. 需要动态增减订阅者的场景运行时动态注册、移除观察者灵活拓展联动能力。e. 主流程与子流程需要解耦的场景主流程只负责核心业务后置动作全部解耦异步处理。1.3 不适合观察者模式的场景a. 联动业务固定、永久不变没有新增订阅需求没必要抽象事件机制属于过度设计。b. 强事务一致性场景观察者多为异步解耦无法保证和主事务强一致。c. 单一固定联动逻辑只有一个后置动作直接方法调用更简单。d. 极高实时、强顺序依赖的链路观察者异步无序无法严格保证执行顺序。2. 观察者模式核心体系与角色2.1 核心特点1.一对多依赖一个主题对应多个观察者2.事件驱动解耦发布者与订阅者无直接依赖3.动态订阅运行时可自由注册、取消观察者4.推拉两种模型推模型主动推送数据拉模型由观察者主动拉取数据。2.2 四大标准角色抽象主题Subject维护观察者集合提供订阅、取消订阅、通知抽象方法。具体主题Concrete Subject状态持有者状态变更后触发全局通知。抽象观察者Observer统一更新接口所有观察者统一行为规范。具体观察者Concrete Observer接收事件执行具体后置业务逻辑。3. 观察者模式标准实战实现推模型·工程最常用3.1 业务场景订单创建成功后自动触发短信通知、积分发放、日志记录多个后置业务。3.2 抽象观察者// 抽象观察者统一更新接口 public interface OrderObserver { void update(String orderNo); }3.3 具体观察者多业务联动// 短信通知观察者 public class SmsObserver implements OrderObserver { Override public void update(String orderNo) { System.out.println(【短信通知】订单orderNo发送下单成功短信); } } // 积分发放观察者 public class ScoreObserver implements OrderObserver { Override public void update(String orderNo) { System.out.println(【积分发放】订单orderNo发放消费积分); } } // 日志记录观察者 public class LogObserver implements OrderObserver { Override public void update(String orderNo) { System.out.println(【日志记录】订单orderNo记录下单操作日志); } }3.4 抽象主题 具体主题import java.util.ArrayList; import java.util.List; // 抽象主题 public abstract class OrderSubject { // 维护所有订阅的观察者 protected ListOrderObserver observerList new ArrayList(); // 订阅 public void attach(OrderObserver observer){ observerList.add(observer); } // 取消订阅 public void detach(OrderObserver observer){ observerList.remove(observer); } // 通知抽象方法 public abstract void notifyObserver(String orderNo); } // 具体主题订单业务主体 public class OrderServiceImpl extends OrderSubject { // 核心业务创建订单 public void createOrder(String orderNo){ System.out.println(【核心业务】订单创建成功orderNo); // 状态变更统一通知所有观察者 notifyObserver(orderNo); } Override public void notifyObserver(String orderNo) { for (OrderObserver observer : observerList) { observer.update(orderNo); } } }3.5 客户端调用public class ObserverClient { public static void main(String[] args) { // 1. 创建主题 OrderServiceImpl orderService new OrderServiceImpl(); // 2. 注册所有观察者可动态增减 orderService.attach(new SmsObserver()); orderService.attach(new ScoreObserver()); orderService.attach(new LogObserver()); // 3. 执行核心业务自动触发所有联动 orderService.createOrder(ORD20260707001); } }4 观察者模式标准实战实现拉模型特点主题只推送变更通知不传递完整数据观察者收到通知后主动从主题拉取最新业务数据。优点按需拉取、灵活、适配复杂数据场景避免无效数据推送。缺点观察者依赖主题查询接口轻微增加耦合多一次查询开销。4.1 拉模型业务场景沿用订单创建场景主题仅推送「订单状态变更通知」不传递订单数据各类观察者收到通知后主动向订单主题拉取订单详情完成对应业务处理。4.2 拉模型核心代码实现// 抽象观察者拉模型无参数更新方法 public interface OrderPullObserver { // 仅接收变更通知无数据传递 void update(); } // 抽象主题提供数据查询接口 import java.util.ArrayList; import java.util.List; public abstract class OrderPullSubject { // 维护订阅观察者 protected ListOrderPullObserver observerList new ArrayList(); // 缓存最新订单号状态数据 protected String latestOrderNo; // 订阅、取消订阅 public void attach(OrderPullObserver observer){ observerList.add(observer); } public void detach(OrderPullObserver observer){ observerList.remove(observer); } // 通知抽象方法 public abstract void notifyObserver(); // 核心提供数据拉取接口供观察者主动查询 public String getLatestOrderNo(){ return this.latestOrderNo; } } // 具体主题 public class OrderPullServiceImpl extends OrderPullSubject { // 创建订单更新状态并触发通知 public void createOrder(String orderNo){ System.out.println(【拉模型核心业务】订单创建成功 orderNo); // 更新主题最新状态 this.latestOrderNo orderNo; // 仅推送通知不传递数据 notifyObserver(); } Override public void notifyObserver() { for (OrderPullObserver observer : observerList) { observer.update(); } } } // 具体观察者短信通知主动拉取数据 public class PullSmsObserver implements OrderPullObserver { // 持有主题引用用于拉取数据 private OrderPullSubject subject; public PullSmsObserver(OrderPullSubject subject){ this.subject subject; } Override public void update() { // 主动拉取主题最新数据 String orderNo subject.getLatestOrderNo(); System.out.println(【拉模型-短信通知】订单orderNo发送下单成功短信); } } // 具体观察者积分发放主动拉取数据 public class PullScoreObserver implements OrderPullObserver { private OrderPullSubject subject; public PullScoreObserver(OrderPullSubject subject){ this.subject subject; } Override public void update() { String orderNo subject.getLatestOrderNo(); System.out.println(【拉模型-积分发放】订单orderNo发放消费积分); } }4.3 拉模型客户端测试public class PullObserverClient { public static void main(String[] args) { // 1. 创建主题 OrderPullServiceImpl orderService new OrderPullServiceImpl(); // 2. 注册观察者注入主题用于拉取数据 orderService.attach(new PullSmsObserver(orderService)); orderService.attach(new PullScoreObserver(orderService)); // 3. 触发业务推送空通知 orderService.createOrder(ORD20260707002); } }5. 推模型 vs 拉模型核心区别面试必背5.1 推模型特点主题主动推送完整数据给观察者。优点观察者无需依赖主题、无需主动查询效率高。缺点数据冗余、灵活性差推送过多无用字段。5.2 拉模型特点主题只推送变更通知观察者主动拉取详细数据。优点按需拉取、灵活、适配复杂数据场景。缺点观察者依赖主题接口增加耦合、多一次查询开销。6. 观察者模式优缺点6.1 优点1. 彻底解耦发布与订阅主流程无需关心后置业务符合单一职责。2. 完美符合开闭原则新增联动业务只需新增观察者无需改主流程。3. 动态订阅、灵活拓展运行时可随意注册、注销观察者。4. 事件统一分发统一后置处理入口便于统一监控、异步、降级。6.2 缺点针对于上述案例1. 默认同步阻塞、主流程变慢所有观察者串行执行任意一个卡顿拖垮主流程。2. 无事务、无重试、易丢失事件内存级事件重启丢失、异常中断无补偿。3. 多级联动排查困难观察者嵌套订阅会形成隐式链路排查链路复杂。4. 内存泄漏风险观察者未及时注销集合常驻内存。7. 企业级成熟实现SpringBoot异步事件监听生产落地版7.1 业务场景订单创建成功后自动触发短信通知、积分发放、日志记录多个后置业务。前面的纯JDK手动实现属于教学版同步阻塞写法存在严重问题串行执行、主流程阻塞、单个观察者异常雪崩、无法异步、无法统一管控。Springboot架构下的生产环境大多使用Spring Event事件机制底层就是观察者模式自动管理订阅、支持异步、异常隔离、事务监听、完全解耦是企业标准落地方案。7.2 企业级落地优势对比原生手写a.彻底异步非阻塞支持全局/指定监听异步不卡主业务RTb.异常隔离单个监听失败不影响其他观察者c.无需手动维护观察者集合Spring IOC自动注册、自动销毁、无内存泄漏d.支持事务绑定可实现「事务成功后再发布事件」解决数据不一致e.原生支持有序、同步/异步混用工程极度成熟。7.3 业务场景订单创建成功后异步并行执行短信通知、积分发放、操作日志记录不阻塞订单创建主流程单个业务失败互不影响。6.4 第一步定义订单事件事件主体-承载数据import org.springframework.context.ApplicationEvent; /** * 订单创建事件消息载体 * 对应观察者模式主题状态数据 */ public class OrderCreateEvent extends ApplicationEvent { // 业务数据 private String orderNo; public OrderCreateEvent(Object source, String orderNo) { super(source); this.orderNo orderNo; } public String getOrderNo() { return orderNo; } }7.5 第二步定义多个观察者事件监听器所有监听器自动订阅事件完全符合一对多观察者模型import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * 短信通知观察者 */ Component public class SmsEventListener { // 异步注解 Async // 监听器注解并指明接收的消息体类 EventListener(OrderCreateEvent.class) public void handleSms(OrderCreateEvent event) { try { String orderNo event.getOrderNo(); // 模拟远程调用发短信 System.out.println(【异步短信通知】订单 orderNo 发送下单短信线程 Thread.currentThread().getName()); } catch (Exception e) { // 生产可扩展失败入队、重试、告警 System.err.println(短信发送失败); } } } /** * 积分发放观察者 */ Component public class ScoreEventListener { // 异步注解 Async // 监听器注解并指明接收的消息体类 EventListener(OrderCreateEvent.class) public void handleScore(OrderCreateEvent event) { try { String orderNo event.getOrderNo(); System.out.println(【异步积分发放】订单 orderNo 发放积分线程 Thread.currentThread().getName()); } catch (Exception e) { System.err.println(积分发放失败); } } } /** * 日志记录观察者可同步执行保证一定落地 */ Component public class LogEventListener { // 监听器注解并指明接收的消息体类 EventListener(OrderCreateEvent.class) public void handleLog(OrderCreateEvent event) { String orderNo event.getOrderNo(); System.out.println(【同步日志记录】订单 orderNo 记录操作日志); } }7.6 第三步事件发布器主题Subject发布者完全不依赖任何观察者彻底解耦、开闭原则满分import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import javax.annotation.Resource; Service public class OrderService { // 事件分发器主题通知器 Resource private ApplicationContext applicationContext; /** * 核心下单业务 */ Transactional(rollbackFor Exception.class) public void createOrder(String orderNo) { // 1. 核心主业务DB落库、事务 System.out.println(【主业务】订单落库成功 orderNo 主线程 Thread.currentThread().getName()); // 2. 发布事件自动通知所有监听该类的观察者无需手动遍历 applicationContext.publishEvent(new OrderCreateEvent(this, orderNo)); // 3. 主流程直接结束不阻塞 System.out.println(【主流程结束】响应客户端后置业务异步执行); } }7.7 第四步开启异步启动类注解import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; SpringBootApplication EnableAsync // 开启Spring异步线程池支持 public class ObserverApplication { public static void main(String[] args) { SpringApplication.run(ObserverApplication.class, args); } }7.8 执行结果 核心特性证明a.主线程瞬间结束接口响应极快b. 短信、积分在独立子线程并行执行互不阻塞c. 日志同步落地保证可靠业务后置异步解耦d. 任意一个监听异常不会崩掉其他监听和主流程。8、企业级进阶事务绑定事件解决生产最大BUG普通EventListener会出现事务未提交监听先执行查不到数据的严重生产问题。企业级终极方案事务提交后再发布事件import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.transaction.event.TransactionPhase; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; Component public class OrderFinalEventListener { /** * 仅在当前事务【成功提交后】异步执行 * 生产最强、最稳、无数据不一致 */ Async TransactionalEventListener(phase TransactionPhase.AFTER_COMMIT) public void afterOrderCommit(OrderCreateEvent event) { System.out.println(【事务提交后异步执行】最终一致性后置业务 event.getOrderNo()); } }对于TransactionalEventListener注解phase翻译过来就是阶段、阶段时机专门控制事件在事务的哪个阶段执行监听逻辑如下是各种策略// 事务提交后执行最常用 TransactionalEventListener(phase TransactionPhase.AFTER_COMMIT) // 事务回滚后执行补偿日志、告警 TransactionalEventListener(phase TransactionPhase.AFTER_ROLLBACK) // 事务完成无论成功/失败 TransactionalEventListener(phase TransactionPhase.AFTER_COMPLETION)9. 企业版 VS 手写基础版核心对比基础手写版教学废弃- 全部同步串行、主接口卡顿 - 一个报错全部崩、无隔离 - 手动维护List容易内存泄漏 - 无法结合事务、生产极易出BUG - 新增观察者需要改代码、不优雅Spring Event 企业版生产首选- 异步并行、零阻塞、接口RT极低 - 每个监听器独立异常隔离 - Spring自动注册销毁、无内存泄漏 - 支持事务前/后/回滚精准触发 - 新增后置业务只加监听器不改主代码完美开闭。10. 工程常见问题与解决方案10.1 同步阻塞导致主接口RT高解决方案引入线程池异步执行观察者逻辑主流程不阻塞。10.2 事件丢失、异常雪崩解决方案每个观察者独立try-catch隔离单个失败不影响其他订阅者。10.3 内存泄漏解决方案组件销毁时主动detach注销观察者避免集合常驻引用。10.4 执行顺序不可控解决方案拓展有序观察者、自定义权重排序控制执行优先级。11. 重点拓展观察者模式 与 消息队列MQ深度对比面试超级高频观察者模式能不能替代MQMQ和观察者的本质区别是什么11.1 核心关系MQ本质就是「分布式观察者模式」本地观察者模式内存级、进程内、同步/异步事件分发。消息队列MQ跨进程、跨服务、分布式、可持久化的高级观察者模式。11.2 详细区别工程落地必背a. 作用范围不同观察者单机进程内只解耦本地代码逻辑。MQ消息队列分布式跨服务解耦微服务之间调用。b. 数据可靠性完全不同观察者无持久化重启丢失、异常丢失、无重试。MQ持久化消息、ACK确认、重试机制、死信队列保证消息可靠投递。c. 性能削峰能力不同观察者无削峰瞬时流量全部压给订阅者。MQ流量削峰、异步缓冲、堆积能力抗高并发。d. 解耦层级不同观察者代码层级解耦。MQ服务架构层级解耦。e. 扩展性不同观察者单机订阅数量有限。MQ支持多消费者集群、广播、分片、重试、延迟队列。11.3 落地选型规范企业级标准使用本地观察者模式单机业务、无需持久化、事件允许丢失、简单后置联动。必须升级为MQ消息队列跨服务联动、需要可靠投递、高并发削峰、需要重试恢复、分布式场景。12. 高频面试易混模式区分12.1 观察者模式 vs 策略模式观察者一对多事件通知发布触发多个后置动作侧重事件联动。策略模式一对一算法替换动态切换单一执行逻辑侧重算法替换。12.2 观察者模式 vs 中介者模式观察者一对多主题统一通知无复杂交互。中介者多对多复杂交互所有对象通过中介通信彻底消除互相依赖。13. 主流框架源码落地面试高频13.1 JDK 原生ObserverJDK 内置 Observer接口、Observable 主题类是标准的观察者模式原生实现已过时但面试常问。13.2 Spring事件机制经典落地Spring ApplicationEvent、ApplicationListener、ApplicationContext完全是观察者模式发布事件 → 容器统一分发 → 所有监听器自动消费是工程中最常用的本地观察者实现。13.3 注册器、监控、配置中心Nacos、Eureka服务上下线通知、配置变更推送底层全部基于观察者事件驱动。14. 核心总结观察者模式的核心本质事件驱动、一对多联动、发布订阅解耦是所有异步事件机制、消息机制的底层原型。工程落地核心原则1. 单机简单联动、允许轻微丢事件优先观察者模式线程池异步轻量高效2. 跨服务、高可靠、高并发、分布式场景必须升级 MQ 消息队列3. 观察者是「代码级解耦」MQ是「架构级解耦」二者是演进关系而非替代关系4. 严格做好异步、异常隔离、主动注销规避阻塞、内存泄漏、事件雪崩问题。