自学内容网 自学内容网

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;    // 用户ID

   private 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 {
   @Override
   public 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 {
   @Override
   public 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中
   @Resource
   private 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();
   }
}

// 在观察者中指定使用自定义线程池
package com.boke.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class AsyncConfig {
   // 自定义异步线程池
   @Bean(name = "observerAsyncExecutor")
   public Executor asyncExecutor() {
          ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
          // 核心线程数
          executor.setCorePoolSize(5);
          // 最大线程数
          executor.setMaxPoolSize(10);
          // 队列容量
          executor.setQueueCapacity(25);
          // 线程名称前缀
          executor.setThreadNamePrefix("Observer-Async-");
          // 线程空闲时间(秒)
          executor.setKeepAliveSeconds(60);
          // 拒绝策略:当线程池满时,直接在调用线程执行(避免丢弃任务)
          executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy);
          // 初始化线程池
          executor.initialize();
          return executor;
   }
}

// 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 ("observerAsyncExecutor") // @Async 指定线程池名称,避免使用默认线程池
   @Override
   public 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内置的事件发布器
   @Autowired
   private 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 {
   @Autowired
   private 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 {
   @Override
   public 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 {
   @Override
   public 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 {
   @Override
   public 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 {
   @Override
   public 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 {
   @Override
   public void refund(CancelOrderBO bo) {
       System.out.printf("原路退回:给用户[%s]的订单[%s]退款[%s]元%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());
   }
}

// 余额退回策略
public class BalanceRefundStrategy implements RefundStrategy {
   @Override
   public void refund(CancelOrderBO bo) {
       System.out.printf("余额退回:给用户[%s]的订单[%s]退款[%s]元到余额%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());
   }
}

// 观察者集成策略模式
@Component
public class AccountCancelOrderListener implements CancelOrderListener {
   @Override
   public 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 {
   // 模板方法:定义固定流程
   @Override
   public 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 {
   @Override
   protected void doProcess(CancelOrderBO bo) {
       System.out.printf("账户业务:给用户[%s]的订单[%s]退款[%s]元%n", bo.getUserId(), bo.getOrderNo(), bo.getAmount());
   }
}

// 库存观察者(子类实现差异化逻辑)
@Component
public class StorageCancelOrderObserver extends AbstractCancelOrderObserver {
    @Override
    protected void doProcess(CancelOrderBO bo) {
        System.out.printf("库存业务:恢复订单[%s]的商品库存%n", bo.getOrderNo());
    }
}

六、总结:观察者模式的本质是 “解耦通知”

观察者模式的核心不是 “如何通知观察者”,而是 “如何在通知的同时,保持被观察者与观察者的低耦合”—— 它通过定义 “发布 - 订阅” 接口,让被观察者只关心 “发布事件”,观察者只关心 “订阅并处理事件”,两者无需知道对方的具体实现。

在实际项目中,无需纠结 “必须用哪种写法”,而是根据场景选择:

  • 快速开发、SpringBoot 项目:优先用 “注入接口”;

  • 复杂场景(异步、事务):用 “Spring 事件”;

  • 学习原理、维护旧代码:了解 “JDK 原生” 和 “手动组装 Map”。

掌握观察者模式,不仅能优化订单取消、支付成功等业务的代码结构,更能理解框架底层的设计逻辑(如 Spring 事件、MQ 消息),为后续学习更复杂的分布式架构打下基础。


原文地址:https://blog.csdn.net/qq_46601365/article/details/151749524

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!