以下是为你精心撰写的 《Java 观察者模式深度学习指南》,专为 Java 后端开发者打造,内容涵盖:定义、作用、与事件驱动架构的深度对比、真实业务场景、JDK 原生实现、Spring 事件机制、Reactive 响应式演进、避坑指南、最佳实践、面试高频题,所有示例均含中文注释,可直接用于项目重构、异步解耦、消息通知、日志采集、系统监控等核心场景。
📘 Java 观察者模式深度学习指南
—— 从“主动轮询”到“事件驱动”的架构革命
作者:Java 后端架构实战导师
适用对象:Java 后端开发者(Spring Boot / 微服务 / 异步系统 / 消息队列 / 监控系统)
目标:彻底掌握“一个对象状态改变,自动通知所有依赖它的对象”的核心模式,告别“循环调用”和“紧耦合”
核心原则:“我不是通知你,我只说‘我变了’,你爱听不听。”
✅ 一、什么是观察者模式?
📌 定义:
观察者模式(Observer Pattern) 是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象(主题 / Subject)的状态发生改变时,所有依赖它的对象(观察者 / Observer)都会自动收到通知并更新。
🔍 核心思想:
- 主题(Subject):被观察的对象,维护一个观察者列表
- 观察者(Observer):依赖主题的对象,实现更新接口
- 松耦合通信:主题不知道观察者是谁,只负责“通知”
- 自动通知:主题状态变化 → 自动调用所有观察者的
update()方法 - 动态注册:观察者可以随时添加或移除
💡 一句话记忆:
“我不是打电话告诉你,我发个朋友圈,你关注我,自然就看到了。”
🆚 观察者模式 vs 轮询(Polling)
| 方式 | 轮询(Polling) | 观察者模式 |
|---|---|---|
| 机制 | 主动频繁查询“有没有变化” | 被动等待“有变化就通知” |
| 效率 | 低(即使没变化也查) | 高(只在变化时触发) |
| 实时性 | 延迟高(取决于轮询间隔) | 实时(变化即通知) |
| 耦合度 | 高(调用方知道被调用方) | 低(主题和观察者互不依赖) |
| 资源消耗 | 高(CPU、网络) | 低(仅触发时消耗) |
| 典型场景 | 旧系统、简单脚本 | 现代系统、事件驱动 |
✅ 经典比喻:
你订阅了“苹果发布会”:
- 轮询:你每分钟刷新一次官网看有没有更新 → 浪费流量和时间
- 观察者:苹果官网发布时,自动给你发邮件/短信 → 高效、精准、及时
✅ 二、观察者模式有什么作用?(Why Use Observer Pattern?)
| 作用 | 说明 |
|---|---|
| ✅ 实现松耦合 | 主题和观察者完全解耦,可独立演化 |
| ✅ 支持一对多通信 | 一个主题可通知多个观察者,无需修改主题代码 |
| ✅ 提高系统响应性 | 状态变化立即触发,无需轮询延迟 |
| ✅ 支持广播机制 | 一次通知,多个模块响应(如日志、缓存、通知) |
| ✅ 支持异步解耦 | 可将通知放入线程池、消息队列,实现异步处理 |
| ✅ 提高可扩展性 | 新增观察者只需注册,无需修改主题 |
| ✅ 符合开闭原则 | 新增观察者不修改主题代码 |
| ✅ 支撑事件驱动架构(EDA) | 是现代微服务、消息系统、响应式编程的基础 |
💡 经典名言:
“Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified automatically.”
——《Design Patterns: Elements of Reusable Object-Oriented Software》
✅ 三、观察者模式的典型使用场景(Java 后端真实案例)
| 场景 | 说明 | 是否推荐使用观察者模式 |
|---|---|---|
| ✅ 订单创建后发送通知 | 创建订单 → 发短信、发邮件、记日志、更新积分 | ✅ 强烈推荐 |
| ✅ 用户注册后激活账户 | 注册成功 → 发验证码、发欢迎邮件、推送APP消息 | ✅ 强烈推荐 |
| ✅ 缓存失效更新 | 数据库更新 → 清除Redis缓存 | ✅ 强烈推荐 |
| ✅ 系统监控告警 | CPU > 90% → 发钉钉、发邮件、触发自动扩容 | ✅ 推荐 |
| ✅ 审计日志记录 | 所有写操作 → 自动记录操作人、时间、IP | ✅ 推荐 |
| ✅ 数据同步 | 主库更新 → 同步到ES、Kafka、HBase | ✅ 推荐 |
| ✅ 配置变更通知 | Nacos配置更新 → 刷新本地缓存、重启服务 | ✅ 推荐 |
| ✅ 支付成功通知 | 支付成功 → 更新订单状态、通知商户、发放优惠券 | ✅ 推荐 |
| ✅ 前端WebSocket推送 | 后端事件触发 → 推送给所有在线用户 | ✅ 推荐 |
| ❌ 简单状态查询 | 获取用户余额 | ❌ 直接调用方法 |
| ❌ 同步业务调用 | 下单 → 扣库存 → 扣库存必须同步完成 | ❌ 用事务或同步调用 |
| ❌ 单点通知 | 只有一个观察者,且必须同步 | ❌ 直接调用更简单 |
✅ 判断标准:
“一个事件发生后,需要通知多个无关模块,且这些模块不需要立即阻塞等待?”
→ 是 → 用观察者模式!
✅ 四、观察者模式的三种实现方式详解(含中文注释)
我们从JDK 原生实现到Spring 事件机制,再到Reactive 响应式演进,逐步深入。
🔹 1. JDK 原生观察者模式(java.util.Observable + Observer)
⚠️ 注意:
Observable已过时,不推荐在新项目中使用,但理解其原理很重要。
import java.util.Observable;
import java.util.Observer;
/**
* 【1】主题(被观察者)—— 继承 Observable
*/
class OrderService extends Observable {
private String orderId;
public void createOrder(String orderId) {
this.orderId = orderId;
System.out.println("🛒 创建订单:" + orderId);
// ✅ 关键:通知所有观察者
setChanged(); // 标记状态已改变
notifyObservers(orderId); // 传递数据给观察者
}
public String getOrderId() {
return orderId;
}
}
/**
* 【2】观察者1:发送短信
*/
class SmsObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
if (arg instanceof String orderId) {
System.out.println("📱 发送短信:订单 " + orderId + " 已创建");
}
}
}
/**
* 【3】观察者2:发送邮件
*/
class EmailObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
if (arg instanceof String orderId) {
System.out.println("📧 发送邮件:订单 " + orderId + " 已创建");
}
}
}
/**
* 【4】观察者3:记录日志
*/
class LogObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
if (arg instanceof String orderId) {
System.out.println("📝 记录日志:订单 " + orderId + " 创建成功");
}
}
}
/**
* 【5】客户端:注册观察者
*/
public class JdkObserverDemo {
public static void main(String[] args) {
OrderService orderService = new OrderService();
// ✅ 注册多个观察者
orderService.addObserver(new SmsObserver());
orderService.addObserver(new EmailObserver());
orderService.addObserver(new LogObserver());
// ✅ 触发事件:自动通知所有观察者
orderService.createOrder("ORD20240501");
// 输出:
// 🛒 创建订单:ORD20240501
// 📱 发送短信:订单 ORD20240501 已创建
// 📧 发送邮件:订单 ORD20240501 已创建
// 📝 记录日志:订单 ORD20240501 创建成功
}
}
✅ 优点:
- JDK 原生支持,无需依赖
- 简单易懂
❌ 缺点:
Observable是类,不是接口 → 不支持多继承notifyObservers()会同步阻塞调用所有观察者- 无法传递事件类型,只能传
Object - 已被标记为 @Deprecated(Java 9+)
⚠️ 结论:仅用于学习,生产环境禁止使用!
🔹 2. 自定义观察者模式(推荐写法)
使用接口 + 集合实现,更灵活、更现代。
import java.util.ArrayList;
import java.util.List;
/**
* 【1】观察者接口:定义更新方法
*/
interface OrderObserver {
void onOrderCreated(String orderId);
}
/**
* 【2】主题:订单服务
*/
class OrderService {
private List<OrderObserver> observers = new ArrayList<>(); // 维护观察者列表
public void addObserver(OrderObserver observer) {
observers.add(observer);
}
public void removeObserver(OrderObserver observer) {
observers.remove(observer);
}
public void createOrder(String orderId) {
System.out.println("🛒 创建订单:" + orderId);
// ✅ 遍历所有观察者,逐个通知
for (OrderObserver observer : observers) {
observer.onOrderCreated(orderId); // 通知
}
}
}
/**
* 【3】观察者1:发送短信
*/
class SmsObserver implements OrderObserver {
@Override
public void onOrderCreated(String orderId) {
System.out.println("📱 发送短信:订单 " + orderId + " 已创建");
}
}
/**
* 【4】观察者2:发送邮件
*/
class EmailObserver implements OrderObserver {
@Override
public void onOrderCreated(String orderId) {
System.out.println("📧 发送邮件:订单 " + orderId + " 已创建");
}
}
/**
* 【5】观察者3:更新缓存
*/
class CacheObserver implements OrderObserver {
@Override
public void onOrderCreated(String orderId) {
System.out.println("⚡ 清除缓存:订单 " + orderId + " 的缓存已失效");
}
}
/**
* 【6】客户端
*/
public class CustomObserverDemo {
public static void main(String[] args) {
OrderService orderService = new OrderService();
// ✅ 注册观察者
orderService.addObserver(new SmsObserver());
orderService.addObserver(new EmailObserver());
orderService.addObserver(new CacheObserver());
// ✅ 触发事件
orderService.createOrder("ORD20240501");
// 输出:
// 🛒 创建订单:ORD20240501
// 📱 发送短信:订单 ORD20240501 已创建
// 📧 发送邮件:订单 ORD20240501 已创建
// ⚡ 清除缓存:订单 ORD20240501 的缓存已失效
}
}
✅ 优点:
- 完全自定义:可定义任意方法名、参数
- 支持泛型:
Observer<T>,类型安全 - 支持异步:可在
onOrderCreated中开启线程 - 无 JDK 限制:可被多个类实现
⚠️ 缺点:
- 同步阻塞:通知是顺序执行,慢的观察者拖慢整体
- 无错误隔离:一个观察者异常,影响其他观察者
- 无事件类型:一个方法处理所有事件
🔹 3. 观察者模式 + Spring 事件机制(企业级推荐)
Spring 的
ApplicationEvent+@EventListener是观察者模式的最佳实践!
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
/**
* 【1】自定义事件:继承 ApplicationEvent
*/
public class OrderCreatedEvent extends ApplicationEvent {
private final String orderId;
private final String userId;
public OrderCreatedEvent(Object source, String orderId, String userId) {
super(source);
this.orderId = orderId;
this.userId = userId;
}
public String getOrderId() {
return orderId;
}
public String getUserId() {
return userId;
}
}
/**
* 【2】主题:订单服务(发布事件)
*/
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher; // ✅ Spring 自动注入
public void createOrder(String orderId, String userId) {
System.out.println("🛒 创建订单:" + orderId + ",用户:" + userId);
// ✅ 发布事件,无需知道谁监听
eventPublisher.publishEvent(new OrderCreatedEvent(this, orderId, userId));
}
}
/**
* 【3】观察者1:发送短信
*/
@Component
public class SmsObserver {
@EventListener // ✅ 自动监听 OrderCreatedEvent
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("📱 发送短信:订单 " + event.getOrderId() + " 已创建");
}
}
/**
* 【4】观察者2:发送邮件
*/
@Component
public class EmailObserver {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("📧 发送邮件:订单 " + event.getOrderId() + " 已创建");
}
}
/**
* 【5】观察者3:更新缓存(异步)
*/
@Component
public class CacheObserver {
@EventListener // ✅ 异步监听
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("⚡ 清除缓存:订单 " + event.getOrderId() + " 的缓存已失效");
}
}
/**
* 【6】观察者4:记录审计日志(条件监听)
*/
@Component
public class AuditLogObserver {
@EventListener(condition = "#event.userId == 'admin'") // ✅ 条件监听
public void handleAdminOrder(OrderCreatedEvent event) {
System.out.println("📝 审计日志:管理员 " + event.getUserId() + " 创建了订单 " + event.getOrderId());
}
}
/**
* 【7】启动类
*/
@SpringBootApplication
public class SpringObserverDemo {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringObserverDemo.class, args);
OrderService orderService = context.getBean(OrderService.class);
// ✅ 触发事件:自动通知所有监听者
orderService.createOrder("ORD20240501", "user123");
orderService.createOrder("ORD20240502", "admin");
// 输出:
// 🛒 创建订单:ORD20240501,用户:user123
// 📱 发送短信:订单 ORD20240501 已创建
// 📧 发送邮件:订单 ORD20240501 已创建
// ⚡ 清除缓存:订单 ORD20240501 的缓存已失效
//
// 🛒 创建订单:ORD20240502,用户:admin
// 📱 发送短信:订单 ORD20240502 已创建
// 📧 发送邮件:订单 ORD20240502 已创建
// ⚡ 清除缓存:订单 ORD20240502 的缓存已失效
// 📝 审计日志:管理员 admin 创建了订单 ORD20240502
}
}
✅ 优点:
- 完全解耦:发布者和监听者无直接依赖
- 注解驱动:
@EventListener自动注册 - 支持异步:加
@Async实现异步通知 - 支持条件监听:
condition = "#event.userId == 'admin'"灵活过滤 - 支持泛型事件:可定义复杂事件结构
- 支持事务同步/异步:
@EventListener(phase = Phase.AFTER_COMMIT)等 - Spring 官方推荐:所有 Spring Boot 项目都在用!
⚠️ 注意:
- 默认是同步阻塞:监听者方法执行慢,会拖慢主线程
- 异步监听需加
@EnableAsync和@Async
✅ 开启异步监听:
@SpringBootApplication
@EnableAsync // ✅ 启用异步支持
public class SpringObserverDemo { ... }
@Component
public class CacheObserver {
@Async // ✅ 异步执行
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// 非阻塞,不影响订单创建流程
System.out.println("⚡ 异步清除缓存:" + event.getOrderId());
}
}
🔹 4. 观察者模式 + Reactor 响应式演进(现代 Java)
在高并发、流式处理场景下,使用 Reactor 替代传统观察者。
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import java.util.concurrent.atomic.AtomicLong;
/**
* 【1】主题:订单流
*/
public class OrderStream {
private final Flux<String> orderFlux;
public OrderStream() {
AtomicLong counter = new AtomicLong(0);
this.orderFlux = Flux.create(emitter -> {
while (true) {
long id = counter.incrementAndGet();
emitter.next("ORD" + id); // 发送事件
try {
Thread.sleep(1000); // 模拟每秒生成一个订单
} catch (InterruptedException e) {
emitter.error(e);
break;
}
}
});
}
public Flux<String> getOrderStream() {
return orderFlux;
}
}
/**
* 【2】观察者1:短信通知
*/
class SmsObserver {
public void subscribeTo(OrderStream stream) {
stream.getOrderStream()
.subscribe(orderId -> System.out.println("📱 发送短信:" + orderId));
}
}
/**
* 【3】观察者2:日志记录
*/
class LogObserver {
public void subscribeTo(OrderStream stream) {
stream.getOrderStream()
.subscribe(orderId -> System.out.println("📝 记录日志:" + orderId));
}
}
/**
* 【4】客户端
*/
public class ReactiveObserverDemo {
public static void main(String[] args) throws InterruptedException {
OrderStream stream = new OrderStream();
// ✅ 注册观察者(订阅者)
new SmsObserver().subscribeTo(stream);
new LogObserver().subscribeTo(stream);
// 运行10秒
Thread.sleep(10000);
}
}
✅ 优点:
- 背压(Backpressure):消费者慢时,生产者自动降速
- 非阻塞、高并发:基于响应式流(Reactive Streams)
- 支持链式操作:
filter()、map()、flatMap() - 现代 Java 标准:Spring WebFlux、RSocket、Kafka Streams 都基于此
⚠️ 缺点:
- 学习曲线陡峭
- 适用于高吞吐、流式系统,普通业务不必要
✅ 五、观察者模式 vs 事件驱动架构(EDA)对比
| 维度 | 观察者模式 | 事件驱动架构(EDA) |
|---|---|---|
| 范围 | 单进程内 | 跨服务、跨系统 |
| 通信方式 | 方法调用 | 消息队列(Kafka/RabbitMQ) |
| 耦合度 | 低 | 极低(完全解耦) |
| 可靠性 | 同步,可能丢失 | 持久化、重试、确认 |
| 扩展性 | 有限 | 无限(可横向扩展) |
| 典型场景 | 单体应用内通知 | 微服务、分布式系统 |
| 实现 | @EventListener | @KafkaListener、@RabbitListener |
✅ 演进关系:
观察者模式 → Spring 事件 → 消息队列(Kafka)
本质都是“发布-订阅”思想,只是通信范围和可靠性不同
✅ 六、观察者模式的避坑指南(Java 后端高频踩坑)
| 问题 | 原因 | 解决方案 |
|---|---|---|
| ❌ 同步阻塞导致性能差 | 观察者方法执行慢,拖慢主线程 | ✅ 使用 @Async 异步处理 |
| ❌ 异常导致通知中断 | 一个观察者抛异常,其他不执行 | ✅ 每个监听器加 try-catch,或使用 @EventListener 的 fallback |
| ❌ 内存泄漏 | 观察者未注销 | ✅ 在 @PreDestroy 或 DisposableBean 中 removeObserver() |
| ❌ 事件循环依赖 | A 发事件 → B 处理 → B 发事件 → A 处理 | ✅ 设计时避免循环依赖,用事件版本号或去重 |
| ❌ 事件命名混乱 | OrderEvent、OrderCreatedEvent、OrderCreateEvent | ✅ 统一命名规范:{Entity}{Action}Event |
| ❌ 事件数据过大 | 传递整个对象,序列化开销大 | ✅ 只传递必要字段(ID、类型) |
| ❌ 用观察者做同步业务 | 如“扣库存”必须同步完成 | ✅ 用事务或同步调用,观察者只做异步事后处理 |
✅ 七、学习建议与进阶路径
| 阶段 | 建议 |
|---|---|
| 📚 第一周 | 将你项目中“订单创建后发短信、发邮件”的 if-else 重构为 Spring 事件 |
| 📚 第二周 | 用 @EventListener 实现“缓存自动清除”、“审计日志自动记录” |
| 📚 第三周 | 用 @Async + @EventListener 实现异步通知,提升接口响应速度 |
| 📚 第四周 | 阅读 Spring 源码:ApplicationEventMulticaster 如何实现事件分发? |
| 📚 面试准备 | 准备回答: |
“你项目中哪里用了观察者模式?”
“Spring 的 @EventListener 是观察者模式吗?”
“观察者模式和策略模式区别?”
“如何解决观察者模式中的异步和异常问题?” |
✅ 八、观察者模式面试高频题(附答案)
Q1:观察者模式解决了什么问题?
A:解决了“一个对象状态变化,需要通知多个依赖对象”的问题,实现松耦合、自动通知。
Q2:Spring 的 @EventListener 是观察者模式吗?
A:是的!它是观察者模式在 Spring 框架中的注解化实现,发布者发布事件,监听者通过
@EventListener自动订阅。
Q3:观察者模式和策略模式的区别?
A:
- 观察者:“我变了,通知你们”(一对多)
- 策略:“你选哪个算法”(一对一,客户端选择)
→ 一个是“广播”,一个是“选择”
Q4:如何解决观察者模式中的性能问题?
A:
- 使用
@Async实现异步通知- 每个监听器加
try-catch防止异常中断- 只传递必要数据,避免序列化大对象
- 使用消息队列(Kafka)实现跨进程解耦
Q5:什么时候不该用观察者模式?
A:
- 通知必须同步完成(如扣库存)
- 只有一个观察者,且必须立即执行
- 性能要求极高,且事件频率极低(直接调用更简单)
✅ 九、总结:观察者模式选型决策树
graph TD
A[需要一个事件触发多个响应吗?] --> B{是否在同一进程?}
B -->|是| C{是否需要异步?}
C -->|是| D[用 @Async + @EventListener]
C -->|否| E[用 @EventListener]
B -->|否| F[用 Kafka/RabbitMQ 实现事件驱动]
✅ 最终推荐:
- 单体应用、异步通知 → ✅ Spring
@EventListener+@Async(最优雅)- 微服务、跨系统 → ✅ Kafka / RabbitMQ(最可靠)
- 高并发流式处理 → ✅ Reactor / RxJava(最现代)
- 绝对不要:JDK
Observable、同步阻塞通知、循环依赖
✅ 十、结语:真正的高手,不通知别人,只发布一个信号
你不是在学“观察者模式”,你是在学“如何让系统各模块自主响应,而非被动等待”。
✅ 当你看到订单创建后,短信、邮件、缓存、日志、积分、风控自动全部触发,而你只写了一行
publishEvent()时,你已经掌握了事件驱动的精髓。
✅ 当你用 Kafka 实现“支付成功”事件,让 5 个微服务各自消费,互不影响时,你已经是分布式架构师了。
不要为了“用模式”而用模式,
要为了“系统可扩展、可解耦、可响应”而选择它。
949

被折叠的 条评论
为什么被折叠?



