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

Java 设计模式——观察者模式:从 4 种写法到 SpringBoot 进阶

Java 设计模式——观察者模式:从 4 种写法到 SpringBoot 进阶

观察者模式(发布 - 订阅模式)是解决 “一对多” 依赖解耦的核心设计模式 —— 当一个对象(被观察者)状态变化时,需自动通知多个关联对象(观察者),且两者无需知道对方的具体实现。本文不仅拆解 4 种实战写法的代码细节,更会通过原理对比SpringBoot 进阶场景避坑指南,帮你彻底掌握 “如何用观察者模式优化业务逻辑”,尤其是在复杂项目中的灵活应用。

一、观察者模式核心认知:为什么它是 “解耦神器”?

在开始之前,先明确观察者模式的 “不可替代性”—— 它解决的是 “强耦合通知” 的痛点:

  • 传统痛点:若取消订单时直接调用 “库存恢复”“退款” 方法(如orderCancel()中写storageService.increase()accountService.refund()),会导致订单模块与库存、账户模块强耦合,新增 “日志记录” 需求时需修改orderCancel()方法,违反开闭原则;

  • 观察者模式优势:订单模块(被观察者)只需 “发布取消事件”,库存、账户、日志模块(观察者)自行 “订阅事件”,新增 / 删除观察者无需修改被观察者代码,彻底解耦;

  • 框架底层依赖:Spring 的事件机制(如ContextRefreshedEvent)、Redis 的发布订阅、MQ 的消息投递,本质都是观察者模式的延伸。

观察者模式的 4 个核心角色(标准定义,实际场景可灵活简化):

  1. 抽象被观察者(Subject):定义 “添加 / 删除观察者” 和 “发布通知” 的接口(如CancelOrderSubjectaddObserver()process());

  2. 具体被观察者(ConcreteSubject):实现抽象被观察者,状态变化时通知所有注册的观察者(如取消订单时的CancelOrderSubject);

  3. 抽象观察者(Observer):定义 “接收通知” 的接口(如CancelOrderObserverprocess());

  4. 具体观察者(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注解可能导致后续维护困难(新增观察者需调整所有顺序号)。

解决方案

  • 若依赖关系明确,拆分事件类型(如OrderCancelRefundEventOrderCancelPointEvent),通过事件发布顺序控制;

  • 若依赖复杂,使用状态机或工作流(如 Flowable)管理业务流程,而非依赖观察者顺序。

2. 防止观察者抛出异常导致连锁失败

问题:若一个观察者抛出未捕获异常,会中断后续观察者的执行(如库存业务抛异常,导致日志观察者无法执行)。

解决方案

  • 观察者内部捕获异常并处理(如记录错误日志,不向外抛出);

  • 使用 Spring 事件的AsyncUncaughtExceptionHandler处理异步观察者的异常;

  • 实现ApplicationListener时,在onApplicationEvent()方法外层加 try-catch。

3. 避免过度使用观察者模式

问题:将所有联动操作都用观察者模式,导致业务逻辑分散(如订单取消关联 10 个观察者,排查问题时需找遍所有观察者)。

解决方案

  • 仅对 “松耦合、无强依赖” 的操作使用观察者模式(如日志记录、通知推送);

  • 对 “强依赖、有事务关联” 的操作(如订单取消→库存恢复→退款),优先用领域服务(Domain Service)封装,保证业务逻辑内聚。

4. 异步观察者的线程安全问题

问题:异步观察者若操作共享变量(如静态变量),会出现线程安全问题。

解决方案

  • 异步观察者尽量无状态(不依赖共享变量);

  • 若需共享数据,使用线程安全的容器(如ConcurrentHashMap)或加锁(synchronizedReentrantLock);

  • 结合 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 消息),为后续学习更复杂的分布式架构打下基础。


文章转载自:

http://wTcbKXt4.ktfbL.cn
http://DjSMuMy6.ktfbL.cn
http://OtE0eDYQ.ktfbL.cn
http://JSlsG3hj.ktfbL.cn
http://SET9worA.ktfbL.cn
http://zOuV2FsU.ktfbL.cn
http://uBa1UEtf.ktfbL.cn
http://fEaYE47N.ktfbL.cn
http://YR9SpICv.ktfbL.cn
http://P7chrOez.ktfbL.cn
http://Ngrxy6uW.ktfbL.cn
http://taBUQ0TJ.ktfbL.cn
http://A2DA6cjS.ktfbL.cn
http://gxmza3lX.ktfbL.cn
http://JzyRnpvQ.ktfbL.cn
http://f15yFpnH.ktfbL.cn
http://VK3f0Xt0.ktfbL.cn
http://t1I1W5Pd.ktfbL.cn
http://IMyJWSRH.ktfbL.cn
http://n0plwtia.ktfbL.cn
http://iEijn2jb.ktfbL.cn
http://ULTBrMHZ.ktfbL.cn
http://FHLlXwLu.ktfbL.cn
http://lEyKc77W.ktfbL.cn
http://PZMBVM1i.ktfbL.cn
http://FzV3Tk1I.ktfbL.cn
http://ujgkDREZ.ktfbL.cn
http://shJyQjIo.ktfbL.cn
http://cEtkzzUy.ktfbL.cn
http://nEfUwSYL.ktfbL.cn
http://www.dtcms.com/a/385939.html

相关文章:

  • “光敏” 黑科技:杜绝手机二维码读取时的 NFC 误触
  • AIGC(生成式AI)试用 36 -- shell脚本(辅助生成)
  • 【计算机网络 | 第17篇】DNS资源记录和报文
  • Flowise安全外网访问指南:基于cpolar的隧道配置详解
  • MySQL OCP认证[特殊字符]Oracle OCP认证
  • Springboot使用Freemark模板生成XML数据
  • 【数据工程】 10. 半结构化数据与 NoSQL 数据库
  • HarmonyOS应用开发:深入ArkUI声明式开发与性能优化实践
  • Vue: 组件注册
  • 408考研计算机网络第38题真题解析(2024)
  • Uni-app 生命周期全解析
  • JavaEE开发技术(第一章:Servlet基础)
  • 【数据结构】跳表
  • 设计模式-桥接模式02
  • Linux 基础命令详解与学习笔记
  • 设计模式(C++)详解——桥接模式(2)
  • 鹧鸪云光储流程系统:以智能仓储管理,驱动项目高效协同
  • DIY Linux 桌面:WiFi 管理器
  • 从 Pump.fun「直播」看热点币的生与死
  • 《算法闯关指南:优选算法-双指针》--05有效三角形的个数,06查找总价值为目标值的两个商品
  • Java List 详解:从基础到进阶的全面指南
  • 【问题】自启动的容器在开机重启后 都退出了,未能正常启动
  • 苹果手机上有没有可以定时提醒做事的工具
  • blender多个动作导入到unity
  • 通过adb dump activity的configChanges配置
  • 智能语音机器人如何提升语音交互机器人的交互能力?
  • 一文读懂Docker:从入门到实践
  • 控制IP端口访问的方法
  • VS2017 下openssl-1.1.1+ libwebsockets-4.0.0 编译
  • 从 “无感服务” 到 “情感连接”:智慧园区如何用科技重构企业归属感