Java 设计模式——观察者模式:从 4 种写法到 SpringBoot 进阶
Java 设计模式——观察者模式:从 4 种写法到 SpringBoot 进阶
观察者模式(发布 - 订阅模式)是解决 “一对多” 依赖解耦的核心设计模式 —— 当一个对象(被观察者)状态变化时,需自动通知多个关联对象(观察者),且两者无需知道对方的具体实现。本文不仅拆解 4 种实战写法的代码细节,更会通过原理对比、SpringBoot 进阶场景、避坑指南,帮你彻底掌握 “如何用观察者模式优化业务逻辑”,尤其是在复杂项目中的灵活应用。
一、观察者模式核心认知:为什么它是 “解耦神器”?
在开始之前,先明确观察者模式的 “不可替代性”—— 它解决的是 “强耦合通知” 的痛点:
-
传统痛点:若取消订单时直接调用 “库存恢复”“退款” 方法(如
orderCancel()
中写storageService.increase()
和accountService.refund()
),会导致订单模块与库存、账户模块强耦合,新增 “日志记录” 需求时需修改orderCancel()
方法,违反开闭原则; -
观察者模式优势:订单模块(被观察者)只需 “发布取消事件”,库存、账户、日志模块(观察者)自行 “订阅事件”,新增 / 删除观察者无需修改被观察者代码,彻底解耦;
-
框架底层依赖:Spring 的事件机制(如
ContextRefreshedEvent
)、Redis 的发布订阅、MQ 的消息投递,本质都是观察者模式的延伸。
观察者模式的 4 个核心角色(标准定义,实际场景可灵活简化):
-
抽象被观察者(Subject):定义 “添加 / 删除观察者” 和 “发布通知” 的接口(如
CancelOrderSubject
的addObserver()
和process()
); -
具体被观察者(ConcreteSubject):实现抽象被观察者,状态变化时通知所有注册的观察者(如取消订单时的
CancelOrderSubject
); -
抽象观察者(Observer):定义 “接收通知” 的接口(如
CancelOrderObserver
的process()
); -
具体观察者(ConcreteObserver):实现抽象观察者,接收通知后执行具体业务(如
AccountCancelOrderObserver
的退款逻辑)。
二、4 种观察者写法深度解析(附代码 + 原理 + 场景)
以下按 “SpringBoot 实战优先级” 排序,从基础手写到底层框架集成,逐一拆解:
1. 注入接口(推荐首选,SpringBoot 最常用)
核心原理
利用 Spring 的依赖注入特性:将所有实现CancelOrderListener
接口的 Bean 自动注入到List<CancelOrderListener>
中,被观察者(订单模块)只需遍历调用接口方法,无需手动注册观察者。本质是 “接口驱动 + Spring 自动装配” 的简化观察者模式。
代码实现(完整实战)
// 1. 业务数据类:传递取消订单的核心信息
package com.boke.desginpattern.bo;import lombok.Data;@Data
public class CancelOrderBO {private String orderNo; // 订单号private Long userId; // 用户IDprivate BigDecimal amount; // 订单金额}// 2. 抽象观察者(接口):定义接收通知的方法
package com.boke.desginpattern.observerinterface.listener;import com.boke.desginpattern.bo.CancelOrderBO;public interface CancelOrderListener {// 观察者的核心方法:处理订单取消通知void cancelOrderProcess(CancelOrderBO cancelOrderBO);
}// 3. 具体观察者1:账户业务(退款)
package com.boke.desginpattern.observerinterface.listener.impl;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerinterface.listener.CancelOrderListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;// @Order(1):指定执行顺序(数字越小越先执行,可选)
@Order(1)
@Component // 标记为Spring Bean,自动被注入到List中
public class AccountCancelOrderListener implements CancelOrderListener {@Overridepublic void cancelOrderProcess(CancelOrderBO cancelOrderBO) {// 实际业务:调用账户服务给用户退款System.out.printf("账户业务:给用户[%s]的订单[%s]退款[%s]元%n", cancelOrderBO.getUserId(), cancelOrderBO.getOrderNo(), cancelOrderBO.getAmount());}
}// 4. 具体观察者2:库存业务(恢复库存)
package com.boke.desginpattern.observerinterface.listener.impl;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerinterface.listener.CancelOrderListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Order(2)
@Component
public class StorageCancelOrderListener implements CancelOrderListener {@Overridepublic void cancelOrderProcess(CancelOrderBO cancelOrderBO) {// 实际业务:调用库存服务恢复商品库存(需关联订单商品表,此处简化)System.out.printf("库存业务:恢复订单[%s]的商品库存%n", cancelOrderBO.getOrderNo());}
}// 5. 被观察者(订单模块):通过Controller接收请求,遍历通知观察者
package com.boke.desginpattern.observerinterface.controller;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerinterface.listener.CancelOrderListener;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;@RestController
@RequestMapping("order")
public class OrderController {// Spring自动注入所有实现CancelOrderListener的Bean到List中@Resourceprivate List<CancelOrderListener> cancelOrderListeners;/*** 取消订单接口(被观察者的核心入口)*/@PostMapping("cancel")public String cancel(@RequestBody CancelOrderBO cancelOrderBO) {System.out.printf("订单业务:开始处理订单[%s]取消%n", cancelOrderBO.getOrderNo());// 通知所有观察者:遍历调用接口方法for (CancelOrderListener listener : cancelOrderListeners) {listener.cancelOrderProcess(cancelOrderBO);}return "订单取消成功";}
}
核心特点
-
✅ 零手动注册:Spring 自动扫描并注入所有观察者,新增观察者只需加
@Component
; -
✅ 支持顺序控制:通过
@Order
注解指定观察者执行顺序(解决 “退款后再恢复库存” 等依赖场景); -
✅ 代码极简:无需定义
Subject
类,直接依赖接口注入,符合 SpringBoot 开发习惯; -
✅ 低耦合:被观察者只依赖接口,不依赖具体观察者,新增业务(如日志记录)无需修改订单代码;
-
❌ 不支持动态注销:观察者一旦注入,运行时无法动态移除(需动态控制可结合
ApplicationContext
手动筛选)。
适用场景
90% 的 SpringBoot 业务场景(如订单取消、支付成功、用户注册后的联动操作),尤其是需要快速开发、低维护成本的场景,是日常开发首选写法。
2. Spring 事件(官方推荐,支持异步 / 事务)
核心原理
基于 Spring 内置的ApplicationEvent
(被观察者)和ApplicationListener
(观察者)机制:
-
事件(Event):继承
ApplicationEvent
,封装被观察者的状态信息(如CancelOrderEvent
); -
监听器(Listener):实现
ApplicationListener
,监听指定事件并处理(如AccountCancelOrderListener
); -
发布者(Publisher):通过
ApplicationEventPublisher
发布事件,Spring 自动通知所有匹配的监听器。
相比 “注入接口”,Spring 事件支持异步执行、事务同步等进阶特性,更适合复杂场景。
代码实现(完整实战)
// 1. 业务数据类:同上(CancelOrderBO)// 2. 被观察者(事件):继承ApplicationEvent,封装事件数据
package com.boke.desginpattern.observerspring.subject;import com.boke.desginpattern.bo.CancelOrderBO;
import org.springframework.context.ApplicationEvent;// 事件 = 被观察者:订单取消事件
public class CancelOrderEvent extends ApplicationEvent {// 构造函数:传入事件源(CancelOrderBO)public CancelOrderEvent(CancelOrderBO source) {super(source);}// 简化获取事件源的方法(避免强制类型转换)public CancelOrderBO getCancelOrderBO() {return (CancelOrderBO) super.getSource();}
}// 3. 具体观察者1:账户业务(异步监听,避免阻塞订单取消)
package com.boke.desginpattern.observerspring.listener;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerspring.subject.CancelOrderEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Component
public class AccountCancelOrderListener implements ApplicationListener<CancelOrderEvent> {// @Async:异步执行(需在启动类加@EnableAsync)@Async@Overridepublic void onApplicationEvent(CancelOrderEvent event) {CancelOrderBO bo = event.getCancelOrderBO();System.out.printf("【异步】账户业务:给用户[%s]的订单[%s]退款[%s]元%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());}
}// 4. 具体观察者2:库存业务(事务同步:订单取消事务提交后再执行)
package com.boke.desginpattern.observerspring.listener;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerspring.subject.CancelOrderEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;@Component
public class StorageCancelOrderListener {// 替代ApplicationListener接口:用@EventListener更灵活// phase = AFTER_COMMIT:订单取消事务提交后再执行(避免事务回滚导致库存错误恢复)@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void onCancelOrderEvent(CancelOrderEvent event) {CancelOrderBO bo = event.getCancelOrderBO();System.out.printf("【事务提交后】库存业务:恢复订单[%s]的商品库存%n", bo.getOrderNo());}
}// 5. 事件发布者:封装发布逻辑(也可直接在Controller中注入ApplicationEventPublisher)
package com.boke.desginpattern.observerspring.publisher;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;@Component
public class OrderEventPublisher {// 注入Spring内置的事件发布器@Autowiredprivate ApplicationEventPublisher eventPublisher;// 发布订单取消事件public void publishCancelOrderEvent(CancelOrderBO cancelOrderBO) {eventPublisher.publishEvent(new CancelOrderEvent(cancelOrderBO));}
}// 6. 被观察者(订单模块):带事务的取消订单接口
package com.boke.desginpattern.observerspring.controller;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerspring.publisher.OrderEventPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("order")
public class OrderController {@Autowiredprivate OrderEventPublisher eventPublisher;/*** 取消订单接口(带事务,确保订单状态修改成功后再通知观察者)*/@PostMapping("cancel")@Transactional // 订单取消的事务注解public String cancel(@RequestBody CancelOrderBO cancelOrderBO) {System.out.printf("订单业务:开始处理订单\[%s]取消(事务中)%n", cancelOrderBO.getOrderNo());// 1. 先修改订单状态为“已取消”(实际业务:调用OrderService修改数据库)// orderService.updateStatus(cancelOrderBO.getOrderNo(), OrderStatus.CANCELED);// 2. 发布事件(若用@TransactionalEventListener,事件会在事务提交后触发)eventPublisher.publishCancelOrderEvent(cancelOrderBO);return "订单取消成功";}
}
核心进阶特性
-
异步监听:通过
@Async
注解实现观察者异步执行(如退款逻辑不阻塞订单取消接口响应),需在启动类加@EnableAsync
; -
事务同步:通过
@TransactionalEventListener
指定事件触发时机(如AFTER_COMMIT
:订单事务提交后再恢复库存,避免事务回滚导致数据不一致); -
事件过滤:支持按事件类型、源对象过滤监听器(如
@EventListener(condition = "#event.cancelOrderBO.amount > 100")
,只处理金额大于 100 的订单); -
灵活注册:除了
@Component
自动注册,还可通过ApplicationContext.addApplicationListener()
动态注册监听器。
核心特点
-
✅ 官方原生支持:Spring 内置机制,无需自定义 Subject,稳定性高;
-
✅ 进阶特性丰富:支持异步、事务同步、事件过滤,应对复杂业务;
-
✅ 低耦合:事件发布者与监听器完全解耦,新增监听器无需修改发布者;
-
❌ 代码稍复杂:需定义 Event 类,相比 “注入接口” 多一层封装。
适用场景
复杂业务场景(如需要异步执行、事务同步、动态注册监听器的场景),或遵循 Spring 官方最佳实践的项目,是中大型项目首选写法。
3. JDK 原生(基于 Observable/Observer,了解即可)
核心原理
利用 JDK 内置的java.util.Observable
(抽象被观察者)和java.util.Observer
(抽象观察者):
-
Observable:维护一个观察者列表,提供
addObserver()
(添加观察者)、notifyObservers()
(通知观察者)方法; -
Observer:定义
update()
方法,接收被观察者的通知。
注意:JDK 9 及以上已标记为过时(@Deprecated),推荐用 Spring 事件或java.util.concurrent.Flow
替代,此处仅作学习参考。
代码实现
// 1. 业务数据类:同上(CancelOrderBO)// 2. 具体被观察者:继承Observable
package com.boke.desginpattern.observerjdk.subject;import com.boke.desginpattern.bo.CancelOrderBO;
import java.util.Observable;public class CancelOrderSubject extends Observable {// 处理订单取消,通知观察者public void processCancelOrder(CancelOrderBO cancelOrderBO) {System.out.printf("订单业务:开始处理订单[%s]取消%n", cancelOrderBO.getOrderNo());// 关键步骤:标记被观察者状态已变化(必须调用,否则notifyObservers不生效)super.setChanged();// 通知所有观察者:推模式(传递cancelOrderBO)super.notifyObservers(cancelOrderBO);}
}// 3. 具体观察者1:账户业务(实现Observer)
package com.boke.desginpattern.observerjdk.observer;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerjdk.subject.CancelOrderSubject;
import java.util.Observable;
import java.util.Observer;public class AccountCancelOrderObserver implements Observer {@Overridepublic void update(Observable o, Object arg) {// 过滤事件源:只处理CancelOrderSubject的通知if (o instanceof CancelOrderSubject && arg instanceof CancelOrderBO) {CancelOrderBO bo = (CancelOrderBO);System.out.printf("账户业务:给用户[%s]的订单[%s]退款[%s]元%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());}}
}// 4. 具体观察者2:库存业务(实现Observer)
package com.boke.desginpattern.observerjdk.observer;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerjdk.subject.CancelOrderSubject;
import java.util.Observable;
import java.util.Observer;public class StorageCancelOrderObserver implements Observer {@Overridepublic void update(Observable o, Object arg) {if (o instanceof CancelOrderSubject && arg instanceof CancelOrderBO) {CancelOrderBO bo = (CancelOrderBO) arg;System.out.printf("库存业务:恢复订单[%s]的商品库存%n", bo.getOrderNo());}}
}// 5. 手动注册观察者(需在启动时初始化)
package com.boke.desginpattern.observerjdk.holder;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerjdk.observer.AccountCancelOrderObserver;
import com.boke.desginpattern.observerjdk.observer.StorageCancelOrderObserver;
import com.boke.desginpattern.observerjdk.subject.CancelOrderSubject;public class SubjectHolder {// 单例持有被观察者实例private static final CancelOrderSubject SUBJECT = initSubject();private static CancelOrderSubject initSubject() {CancelOrderSubject subject = new CancelOrderSubject();// 手动注册观察者subject.addObserver(new AccountCancelOrderObserver());subject.addObserver(new StorageCancelOrderObserver());return subject;}// 对外提供通知入口public static void processCancelOrder(CancelOrderBO cancelOrderBO) {SUBJECT.processCancelOrder(cancelOrderBO);}
}// 6. 测试接口(与 Spring 集成)
package com.boke.desginpattern.observerjdk.controller;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observerjdk.holder.SubjectHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("order")
public class OrderController {@PostMapping ("cancel")public String cancel(@RequestBody CancelOrderBO cancelOrderBO) {SubjectHolder.processCancelOrder(cancelOrderBO);return "订单取消成功";}
}
核心特点
-
✅ JDK原生支持:无需引入额外依赖,适合非Spring项目;
-
✅ 原理清晰:手动管理观察者注册,便于理解观察者模式的底层逻辑;
-
❌ 已被过时:JDK 9+标记为@Deprecated,官方不推荐新项目使用;
-
❌ 功能简陋:不支持异步、事务同步,观察者方法固定为`update()`,灵活性低;
-
❌ 手动注册繁琐:新增观察者需修改`initSubject()`方法,违反开闭原则。
适用场景
仅作为学习观察者模式原理的案例,或维护旧项目中已使用该写法的代码,**不推荐新项目使用**。
4. 手动组装Map(基础手写,理解原理)
核心原理
完全手动实现观察者模式的标准角色:自定义`Subject`类维护观察者列表(用`List`或`Map`存储),提供`addObserver()`/`removeObserver()`方法管理观察者,状态变化时遍历通知。本质是“从零构建观察者模式”,适合理解核心逻辑。
代码实现
// 1. 业务数据类:同上(CancelOrderBO)// 2. 抽象观察者(接口)
package com.boke.desginpattern.observermanual.observer;import com.boke.desginpattern.bo.CancelOrderBO;public interface CancelOrderObserver {void process(CancelOrderBO cancelOrderBO);
}// 3. 具体观察者1:账户业务
package com.boke.desginpattern.observermanual.observer.impl;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observermanual.observer.CancelOrderObserver;public class AccountCancelOrderObserver implements CancelOrderObserver {@Overridepublic void process(CancelOrderBO cancelOrderBO) {System.out.printf("账户业务:给用户[%s]的订单[%s]退款[%s]元%n", cancelOrderBO.getUserId(), cancelOrderBO.getOrderNo(), cancelOrderBO.getAmount());}
}// 4. 具体观察者2:库存业务
package com.boke.desginpattern.observermanual.observer.impl;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observermanual.observer.CancelOrderObserver;public class StorageCancelOrderObserver implements CancelOrderObserver {@Overridepublic void process(CancelOrderBO cancelOrderBO) {System.out.printf("库存业务:恢复订单[%s]的商品库存%n", cancelOrderBO.getOrderNo());}
}// 5. 具体被观察者(Subject)
package com.boke.desginpattern.observermanual.subject;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observermanual.observer.CancelOrderObserver;
import java.util.ArrayList;
import java.util.List;public class CancelOrderSubject {// 维护观察者列表(用List存储,也可用Map按类型分类)private final List<CancelOrderObserver> observers = new ArrayList<>();// 添加观察者public void addObserver(CancelOrderObserver observer) {if (observer != null && !observers.contains(observer)) {observers.add(observer);}}// 移除观察者public void removeObserver(CancelOrderObserver observer) {observers.remove(observer);}// 通知所有观察者public void notifyObservers(CancelOrderBO cancelOrderBO) {System.out.printf("订单业务:开始处理订单[%s]取消%n", cancelOrderBO.getOrderNo());for (CancelOrderObserver observer : observers) {observer.process(cancelOrderBO);}}
}// 6. 手动初始化并注册观察者
package com.boke.desginpattern.observermanual.holder;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observermanual.observer.impl.AccountCancelOrderObserver;
import com.boke.desginpattern.observermanual.observer.impl.StorageCancelOrderObserver;
import com.boke.desginpattern.observermanual.subject.CancelOrderSubject;public class SubjectHolder {private static final CancelOrderSubject SUBJECT = initSubject();private static CancelOrderSubject initSubject() {CancelOrderSubject subject = new CancelOrderSubject();// 手动注册观察者subject.addObserver(new AccountCancelOrderObserver());subject.addObserver(new StorageCancelOrderObserver());return subject;}public static void processCancelOrder(CancelOrderBO cancelOrderBO) {SUBJECT.notifyObservers(cancelOrderBO);}
}// 7. 测试接口
package com.boke.desginpattern.observermanual.controller;import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.observermanual.holder.SubjectHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("order")
public class OrderController {@PostMapping("cancel")public String cancel(@RequestBody CancelOrderBO cancelOrderBO) {SubjectHolder.processCancelOrder(cancelOrderBO);return "订单取消成功";}
}
核心特点
-
✅ 原理透明:完全手动实现观察者模式的所有角色,便于理解 “Subject-Observer” 的交互逻辑;
-
✅ 灵活性高:可自定义观察者方法名(如
process()
而非固定update()
),支持按业务扩展; -
❌ 手动注册繁琐:新增 / 删除观察者需修改
initSubject()
方法,维护成本高; -
❌ 无进阶功能:不支持异步、事务同步,需自行实现;
-
❌ 代码冗余:需重复编写 Subject 的观察者管理逻辑,不如 Spring 自动装配高效。
适用场景
学习观察者模式的核心原理(如理解 “如何维护观察者列表”“如何通知观察者”),或在无框架依赖的简单项目中使用,不推荐 SpringBoot 项目使用。
三、4 种观察者写法对比表(实战选型指南)
写法 | 核心优势 | 核心劣势 | 适用场景 | 实战优先级 |
---|---|---|---|---|
注入接口 | 零手动注册、代码极简、支持顺序控制 | 不支持动态注销 | SpringBoot 日常开发(90% 场景) | ★★★★★ |
Spring 事件 | 支持异步 / 事务同步、官方原生、动态注册 | 需定义 Event 类,代码稍复杂 | 中大型项目复杂场景(异步、事务依赖) | ★★★★☆ |
JDK 原生 | JDK 原生支持,无额外依赖 | 已过时、功能简陋、手动注册 | 旧项目维护、原理学习 | ★☆☆☆☆ |
手动组装 Map | 原理透明、自定义程度高 | 手动注册繁琐、无进阶功能、代码冗余 | 观察者模式原理学习、无框架简单项目 | ★★☆☆☆ |
四、观察者模式避坑指南(实战必看)
1. 避免观察者执行顺序依赖
问题:若观察者 A 必须在观察者 B 之前执行(如 “退款后再扣减积分”),直接依赖@Order
注解可能导致后续维护困难(新增观察者需调整所有顺序号)。
解决方案:
-
若依赖关系明确,拆分事件类型(如
OrderCancelRefundEvent
和OrderCancelPointEvent
),通过事件发布顺序控制; -
若依赖复杂,使用状态机或工作流(如 Flowable)管理业务流程,而非依赖观察者顺序。
2. 防止观察者抛出异常导致连锁失败
问题:若一个观察者抛出未捕获异常,会中断后续观察者的执行(如库存业务抛异常,导致日志观察者无法执行)。
解决方案:
-
观察者内部捕获异常并处理(如记录错误日志,不向外抛出);
-
使用 Spring 事件的
AsyncUncaughtExceptionHandler
处理异步观察者的异常; -
实现
ApplicationListener
时,在onApplicationEvent()
方法外层加 try-catch。
3. 避免过度使用观察者模式
问题:将所有联动操作都用观察者模式,导致业务逻辑分散(如订单取消关联 10 个观察者,排查问题时需找遍所有观察者)。
解决方案:
-
仅对 “松耦合、无强依赖” 的操作使用观察者模式(如日志记录、通知推送);
-
对 “强依赖、有事务关联” 的操作(如订单取消→库存恢复→退款),优先用领域服务(Domain Service)封装,保证业务逻辑内聚。
4. 异步观察者的线程安全问题
问题:异步观察者若操作共享变量(如静态变量),会出现线程安全问题。
解决方案:
-
异步观察者尽量无状态(不依赖共享变量);
-
若需共享数据,使用线程安全的容器(如
ConcurrentHashMap
)或加锁(synchronized
、ReentrantLock
); -
结合 Spring 的
ThreadPoolTaskExecutor
自定义线程池,控制异步线程的并发数。
五、观察者模式与其他模式的结合场景
观察者模式很少单独使用,常与以下模式结合,应对更复杂的业务需求:
1. 观察者模式 + 工厂模式
场景:观察者类型动态变化(如根据订单类型,选择不同的观察者)。
实现:用工厂模式创建观察者实例,Subject 从工厂获取观察者列表,而非硬编码注册。
// 观察者工厂
public class CancelOrderObserverFactory {// 根据订单类型获取对应的观察者public static List<CancelOrderListener> getObserversByOrderType(String orderType) {List<CancelOrderListener> observers = new ArrayList<>();if ("VIP_ORDER".equals(orderType)) {observers.add(new VipAccountCancelListener()); // VIP专属退款观察者observers.add(new StorageCancelOrderListener());} else {observers.add(new NormalAccountCancelListener()); // 普通用户退款观察者observers.add(new StorageCancelOrderListener());}return observers;}
}// Subject中使用工厂获取观察者
public class CancelOrderSubject {public void notifyObservers(CancelOrderBO bo) {List<CancelOrderListener> observers = CancelOrderObserverFactory.getObserversByOrderType(bo.getOrderType());for (CancelOrderListener observer : observers) {observer.process(bo);}}
}
2. 观察者模式 + 策略模式
场景:观察者的执行逻辑需动态切换(如退款逻辑有 “原路退回”“余额退回” 两种策略)。
实现:观察者内部集成策略模式,根据业务参数选择不同的执行策略。
// 退款策略接口
public interface RefundStrategy {void refund(CancelOrderBO bo);
}// 原路退回策略
public class OriginalRefundStrategy implements RefundStrategy {@Overridepublic void refund(CancelOrderBO bo) {System.out.printf("原路退回:给用户[%s]的订单[%s]退款[%s]元%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());}
}// 余额退回策略
public class BalanceRefundStrategy implements RefundStrategy {@Overridepublic void refund(CancelOrderBO bo) {System.out.printf("余额退回:给用户[%s]的订单[%s]退款[%s]元到余额%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());}
}// 观察者集成策略模式
@Component
public class AccountCancelOrderListener implements CancelOrderListener {@Overridepublic void process(CancelOrderBO bo) {// 根据订单支付方式选择退款策略RefundStrategy strategy = "ONLINE_PAY".equals(bo.getPayType()) ? new OriginalRefundStrategy() : new BalanceRefundStrategy();strategy.refund(bo);}
}
3. 观察者模式 + 模板方法模式
场景:多个观察者有相同的执行流程(如 “参数校验→业务处理→日志记录”),仅业务处理逻辑不同。
实现:用模板方法模式定义观察者的执行流程,子类仅实现差异化的业务处理逻辑。
// 观察者模板类(抽象类)
public abstract class AbstractCancelOrderObserver implements CancelOrderListener {// 模板方法:定义固定流程@Overridepublic final void process(CancelOrderBO bo) {// 1. 参数校验(固定逻辑)validate(bo);// 2. 业务处理(差异化逻辑,子类实现)doProcess(bo);// 3. 日志记录(固定逻辑)log(bo);}// 固定逻辑:参数校验private void validate(CancelOrderBO bo) {if (bo.getOrderNo() == null || bo.getUserId() == null) {throw new IllegalArgumentException("订单号和用户ID不能为空");}}// 抽象方法:子类实现差异化业务处理protected abstract void doProcess(CancelOrderBO bo);// 固定逻辑:日志记录private void log(CancelOrderBO bo) {System.out.printf("日志记录:订单[%s]取消观察者处理完成%n", bo.getOrderNo());}
}// 账户观察者(子类实现差异化逻辑)
@Component
public class AccountCancelOrderObserver extends AbstractCancelOrderObserver {@Overrideprotected void doProcess(CancelOrderBO bo) {System.out.printf("账户业务:给用户[%s]的订单[%s]退款[%s]元%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());}
}// 库存观察者(子类实现差异化逻辑)
@Component
public class StorageCancelOrderObserver extends AbstractCancelOrderObserver {@Overrideprotected void doProcess(CancelOrderBO bo) {System.out.printf("库存业务:恢复订单[%s]的商品库存%n", bo.getOrderNo());}
}
六、总结:观察者模式的本质是 “解耦通知”
观察者模式的核心不是 “如何通知观察者”,而是 “如何在通知的同时,保持被观察者与观察者的低耦合”—— 它通过定义 “发布 - 订阅” 接口,让被观察者只关心 “发布事件”,观察者只关心 “订阅并处理事件”,两者无需知道对方的具体实现。
在实际项目中,无需纠结 “必须用哪种写法”,而是根据场景选择:
-
快速开发、SpringBoot 项目:优先用 “注入接口”;
-
复杂场景(异步、事务):用 “Spring 事件”;
-
学习原理、维护旧代码:了解 “JDK 原生” 和 “手动组装 Map”。
掌握观察者模式,不仅能优化订单取消、支付成功等业务的代码结构,更能理解框架底层的设计逻辑(如 Spring 事件、MQ 消息),为后续学习更复杂的分布式架构打下基础。