当前位置: 首页 > news >正文

设计模式—观察者模式(超全工程面试+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 第三步:事件发布器(主题Subject)

7.7 第四步:开启异步(启动类注解)

7.8 执行结果 & 核心特性证明

8、企业级进阶:事务绑定事件(解决生产最大BUG)

9. 企业版 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 原生Observer

13.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 List<OrderObserver> 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 List<OrderPullObserver> 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.彻底异步非阻塞:支持全局/指定监听异步,不卡主业务RT;

b.异常隔离:单个监听失败不影响其他观察者;

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深度对比

面试超级高频:观察者模式能不能替代MQ?MQ和观察者的本质区别是什么?

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 原生Observer

JDK 内置 Observer接口、Observable 主题类,是标准的观察者模式原生实现(已过时,但面试常问)。

13.2 Spring事件机制(经典落地)

Spring ApplicationEvent、ApplicationListener、ApplicationContext完全是观察者模式:

发布事件 → 容器统一分发 → 所有监听器自动消费,是工程中最常用的本地观察者实现。

13.3 注册器、监控、配置中心

Nacos、Eureka服务上下线通知、配置变更推送,底层全部基于观察者事件驱动。

14. 核心总结

观察者模式的核心本质:事件驱动、一对多联动、发布订阅解耦,是所有异步事件机制、消息机制的底层原型。

工程落地核心原则:

1. 单机简单联动、允许轻微丢事件:优先观察者模式+线程池异步,轻量高效;

2. 跨服务、高可靠、高并发、分布式场景:必须升级 MQ 消息队列

3. 观察者是「代码级解耦」,MQ是「架构级解耦」,二者是演进关系而非替代关系;

4. 严格做好异步、异常隔离、主动注销,规避阻塞、内存泄漏、事件雪崩问题。

http://www.jsqmd.com/news/1145863/

相关文章:

  • 2026低代码争议刷屏:会被AI取代?真正淘汰的是“玩具级低代码”
  • 苹果系统:sourcetree涉及多个github账号项目
  • 工业设备数据管理中台是什么?有什么功能?
  • 华硕笔记本性能调校神器:G-Helper让你的电脑重获新生
  • HTML5+CSS3+JS小实例:带本地存储的备忘录记事本
  • 三分钟分清 ISTA 1A/2A/3A,包装测试再也不踩坑
  • 网络调试神器 Netcat for Windows:告别网络测试烦恼的命令行瑞士军刀[特殊字符]
  • 2026最新2款AI编程工具平替实测:toB项目vibe coding性能深度对比
  • AI 电动加热手套智能温控功率 MOSFET 完整选型方案
  • 具身智能产业化分水岭:优必选、银河通用、越疆三路竞速,路线差异决定行业天花板
  • 终极指南:使用QRemeshify快速将Blender三角网格转换为完美四边形拓扑
  • Windows网络调试困境:3个Netcat实战场景让你告别图形化工具依赖
  • 华硕笔记本终极性能优化:G-Helper完整指南
  • 外贸SEO案例:一个关键词排上Google首页,年增500万销售额
  • 3分钟解决GitHub龟速问题:智能加速插件让你的开发效率翻倍
  • Origami Simulator终极指南:实时3D折纸模拟的完整体验
  • Linux Shell基础知识点总结与实操演示
  • 揭秘DrawDB:重新定义数据库建模体验的现代解决方案
  • 串口调试方法
  • 耐达讯自动化EtherNet/IP转DeviceNet网关:功能、参数与配置,看这一篇就够了!
  • -python-LangGraph框架(3-34-LangGraph循环实战)
  • 从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚
  • Linux服务器SSH安全实战:从sshd:notty日志分析到自动化封禁
  • Vibe Coding学习(3)--Skills
  • 湖南省内汉王品牌官方授权总代理是哪家企业?
  • RuoYi v4.6.0 SQL注入漏洞CVE-2023-49371:从POC到代码审计的3步深度分析
  • VLA模型文本扰动导致自动驾驶失控的机理与防御
  • 华硕笔记本终极性能优化指南:GHelper轻量级控制工具完全教程
  • 手把手教你学pcie--Vendor ID / Device ID / Class Code(设备的“三件套”)
  • 国产清洁机器人海外卖爆了:为什么电池系统越来越关键?