Java 观察者模式从入门到实战(后端必看,附案例+面试考点)
前言:观察者模式(Observer Pattern)是 Java 行为型设计模式中的核心模式,也被叫做发布 - 订阅模式 ,完美解决了对象间一对多依赖、一个对象状态变更自动通知其他依赖对象的场景问题。
日常开发中,消息推送、事件监听、缓存更新、订单状态同步等场景,都离不开观察者模式;Spring 的事件驱动、RabbitMQ 消息队列、Guava EventBus 等技术,底层也都基于观察者模式思想实现。
本文从痛点问题、核心概念、入门代码、真实业务实战、框架应用、高频面试题全维度拆解,带你彻底吃透观察者模式,新手能直接上手,老手能落地项目,面试轻松拿高分。
一、为什么要学观察者模式?(痛点直击)
先看后端开发中高频遇到的问题,不用观察者模式,代码会变得极其臃肿、耦合严重:
-
订单支付成功后:需要同步更新订单状态、推送支付成功消息、增加用户积分、扣减商品库存,所有逻辑写在一个方法里,代码冗长,新增 / 删除逻辑需修改原有代码;
-
用户注册成功后:需要发送注册短信、初始化用户账户、记录注册日志,业务逻辑高度耦合,违背开闭原则;
-
状态变更通知:一个对象状态改变,多个对象需要感知并执行操作,硬编码调用导致代码难以维护、扩展性极差。
这些问题的核心:对象间强耦合,状态变更无法自动通知,业务逻辑耦合在一起,维护和扩展成本极高。
而观察者模式 ,就是专门解决这类一对多依赖、状态自动通知、业务解耦的问题,将发布者和订阅者完全分离,发布者只负责发布消息,订阅者只负责接收并处理消息,新增订阅逻辑无需修改发布者代码,完美符合开闭原则。
简单理解:观察者模式就像公众号订阅,公众号(被观察者 / 主题)发布文章,所有关注的用户(观察者)都会收到推送,用户可随时订阅 / 取消订阅,公众号无需关心有多少用户关注。
二、观察者模式核心概念与角色
2.1 模式定义
观察者模式:定义对象间的一对多依赖关系,当一个对象(被观察者 / 主题)的状态发生改变时,所有依赖它的对象(观察者)都会收到通知并自动更新。
2.2 核心角色(5 大核心,必记)
-
抽象主题(Subject):也叫抽象被观察者,定义添加、删除、通知观察者的接口,持有观察者集合;
-
具体主题(ConcreteSubject):实现抽象主题接口,维护自身状态,状态变更时通知所有观察者;
-
抽象观察者(Observer):定义收到通知后的更新方法,所有具体观察者必须实现;
-
具体观察者(ConcreteObserver):实现抽象观察者接口,接收到通知后执行业务逻辑;
-
客户端(Client):创建主题和观察者对象,将观察者注册到主题中,触发主题状态变更。
2.3 核心执行流程
-
客户端创建主题对象和多个观察者对象;
-
将观察者注册到主题的观察者列表中;
-
主题状态发生变更,遍历观察者列表,调用每个观察者的更新方法;
-
观察者接收通知,执行自身业务逻辑。
三、观察者模式入门实现(原生 Java,可直接复制)
以订单支付通知为例,实现原生观察者模式,模拟支付成功后,消息服务、积分服务、库存服务自动执行逻辑。
3.1 抽象观察者(Observer)
java
/**
* 抽象观察者
*/
public interface OrderObserver {
/**
* 订单状态更新通知
* @param orderId 订单ID
*/
void update(String orderId);
}
3.2 具体观察者(多个)
java
/**
* 消息服务观察者:发送支付成功短信
*/
@Component
public class MessageObserver implements OrderObserver {
@Override
public void update(String orderId) {
System.out.println("【消息服务】订单:" + orderId + " 支付成功,已发送短信通知");
}
}
/**
* 积分服务观察者:增加用户积分
*/
@Component
public class IntegralObserver implements OrderObserver {
@Override
public void update(String orderId) {
System.out.println("【积分服务】订单:" + orderId + " 支付成功,已增加用户积分");
}
}
/**
* 库存服务观察者:扣减商品库存
*/
@Component
public class StockObserver implements OrderObserver {
@Override
public void update(String orderId) {
System.out.println("【库存服务】订单:" + orderId + " 支付成功,已扣减商品库存");
}
}
3.3 抽象主题(Subject)
java
/**
* 抽象主题(被观察者)
*/
public interface OrderSubject {
// 添加观察者
void addObserver(OrderObserver observer);
// 删除观察者
void removeObserver(OrderObserver observer);
// 通知所有观察者
void notifyObservers(String orderId);
}
3.4 具体主题
java
/**
* 订单主题:具体被观察者
*/
@Component
public class OrderConcreteSubject implements OrderSubject {
// 观察者集合
private List<OrderObserver> observerList<>();
@Override
public void addObserver(OrderObserver observer) {
observerList.add(observer);
}
@Override
public void removeObserver(OrderObserver observer) {
observerList.remove(observer);
}
@Override
public void notifyObservers(String orderId) {
// 遍历通知所有观察者
for (OrderObserver observer : observerList) {
observer.update(orderId);
}
}
}
3.5 客户端测试
java
public class ObserverTest {
public static void main(String[] args) {
// 1. 创建主题(被观察者)
OrderSubject orderSubject = new OrderConcreteSubject();
// 2. 创建观察者并注册到主题
OrderObserver messageObserver = new MessageObserver();
OrderObserver integralObserver = new IntegralObserver();
OrderObserver stockObserver = new StockObserver();
orderSubject.addObserver(messageObserver);
orderSubject.addObserver(integralObserver);
orderSubject.addObserver(stockObserver);
// 3. 模拟订单支付成功,通知所有观察者
System.out.println("订单:ORDER_20240520_001 支付成功");
orderSubject.notifyObservers("ORDER_20240520_001");
}
}
3.6 运行结果
Plain
订单:ORDER_20240520_001 支付成功
【消息服务】订单:ORDER_20240520_001 支付成功,已发送短信通知
【积分服务】订单:ORDER_20240520_001 支付成功,已增加用户积分
【库存服务】订单:ORDER_20240520_001 支付成功,已扣减商品库存
3.7 代码优势
-
解耦彻底:发布者(订单主题)和订阅者(各服务)完全分离,互不依赖;
-
扩展性强:新增 / 删除业务逻辑,只需新增 / 删除观察者,无需修改主题代码;
-
职责单一:每个观察者只负责自身业务逻辑,符合单一职责原则。
四、Java 原生观察者模式(内置 API)
Java 本身提供了java\.util\.Observable(抽象主题)和java\.util\.Observer(抽象观察者)接口,可直接使用,无需自定义接口。
4.1 具体主题(继承 Observable)
java
import java.util.Observable;
/**
* Java原生订单主题
*/
public class JavaOrderSubject extends Observable {
// 订单支付成功,触发通知
public void paySuccess(String orderId) {
// 标记状态已变更
setChanged();
// 通知所有观察者
notifyObservers(orderId);
}
}
4.2 具体观察者(实现 Observer)
java
import java.util.Observable;
import java.util.Observer;
/**
* 原生消息观察者
*/
public class JavaMessageObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
String orderId = (String) arg;
System.out.println("【原生消息服务】订单:" + orderId + " 支付成功,已发送通知");
}
}
注意:Java 内置的 Observable 是类,而非接口,存在单继承局限,实际开发中更推荐自定义接口实现。
五、真实业务实战(Spring Boot + 观察者模式)
实际后端开发中,结合 Spring Boot 实现观察者模式,通过依赖注入管理对象,适配真实业务场景,代码可直接复用。
5.1 实战场景
用户注册成功后,自动执行:
-
发送注册成功短信;
-
初始化用户个人账户;
-
记录用户注册日志。
5.2 核心代码实现
1. 抽象观察者
java
public interface UserRegisterObserver {
void handleRegisterSuccess(Long userId);
}
2. 具体观察者
java
// 短信服务观察者
@Service
public class SmsObserver implements UserRegisterObserver {
@Override
public void handleRegisterSuccess(Long userId) {
System.out.println("【短信服务】用户ID:" + userId + " 注册成功,发送短信通知");
}
}
// 账户初始化观察者
@Service
public class AccountObserver implements UserRegisterObserver {
@Override
public void handleRegisterSuccess(Long userId) {
System.out.println("【账户服务】用户ID:" + userId + " 注册成功,初始化个人账户");
}
}
// 日志记录观察者
@Service
public class LogObserver implements UserRegisterObserver {
@Override
public void handleRegisterSuccess(Long userId) {
System.out.println("【日志服务】用户ID:" + userId + " 注册成功,记录注册日志");
}
}
3. 主题(被观察者)
java
@Service
public class UserRegisterSubject {
@<UserRegisterObserver> registerObservers;
// 用户注册成功,通知所有观察者
public void registerSuccess(Long userId) {
System.out.println("用户ID:" + userId + " 注册成功");
for (UserRegisterObserver observer : registerObservers) {
observer.handleRegisterSuccess(userId);
}
}
}
4. 控制层调用
java
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRegisterSubject userRegisterSubject;
@PostMapping("/register")
public String register(Long userId) {
// 模拟用户注册逻辑
userRegisterSubject.registerSuccess(userId);
return "注册成功";
}
}
5.3 实战亮点
-
完全适配 Spring Boot 开发,通过 @Autowired 自动注入所有观察者;
-
无硬编码,新增业务只需新增观察者类,无需修改原有代码;
-
业务解耦,便于后续维护和扩展。
六、观察者模式在主流框架中的应用
6.1 Spring 事件驱动模型
Spring 的ApplicationEvent(事件)、ApplicationListener(事件监听),就是观察者模式的经典实现:
-
ApplicationEvent:抽象主题; -
ApplicationListener:抽象观察者; -
ApplicationContext:事件发布者,负责通知所有监听器。
6.2 Guava EventBus
Google Guava 提供的 EventBus,是观察者模式的优雅实现,简化了事件注册和通知,无需手动维护观察者列表。
6.3 RabbitMQ/RocketMQ 消息队列
消息队列的发布 - 订阅模式,本质是分布式的观察者模式,生产者(发布者)发送消息,消费者(订阅者)监听并消费消息,实现跨服务的解耦通知。
6.4 Swing/AWT 事件监听
Java 桌面开发中的按钮点击、鼠标移动等事件,都是基于观察者模式实现,组件是被观察者,事件监听器是观察者。
七、观察者模式高频面试考点(必背)
1. 观察者模式和发布 - 订阅模式的区别?
经典面试题,必背:
-
观察者模式:一对多,直接耦合,主题和观察者相互感知,在同一进程内;
-
发布 - 订阅模式:多对多,完全解耦,通过中间件(消息队列、事件总线)通信,发布者和订阅者互不感知,可跨进程 / 跨服务。
2. 观察者模式的优缺点?
优点:
-
解耦:发布者和订阅者分离,互不依赖;
-
可扩展:新增订阅逻辑无需修改原有代码;
-
自动通知:状态变更自动通知所有依赖对象。
缺点:
-
观察者过多时,通知效率降低;
-
观察者之间无依赖,无法感知相互执行顺序;
-
若观察者和主题循环依赖,可能导致系统崩溃。
3. 观察者模式和责任链模式的区别?
-
观察者模式:一对多,同时通知所有观察者;
-
责任链模式:一对一,依次传递请求,一个请求被一个处理器处理。
4. 什么场景适合用观察者模式?
-
一个对象状态变更,需要通知其他多个对象;
-
业务解耦,拆分耦合的业务逻辑;
-
事件监听、消息推送、状态同步场景。
八、总结
观察者模式是后端开发必掌握的解耦神器 ,核心思想就是发布 - 订阅,彻底解决对象间一对多的依赖耦合问题,让代码更易维护、更易扩展。
新手学习从原生代码入手,理解核心角色和执行流程;实战开发结合 Spring Boot、EventBus 等工具,落地真实业务;面试重点掌握和发布 - 订阅模式的区别、优缺点、适用场景。
实际开发中,小场景用自定义观察者模式,大场景 / 分布式场景用消息队列,灵活选择,让代码更优雅、更健壮。