设计模式之观察者模式

观察者模式(Observer Pattern)是一种经典的行为型设计模式 ,它解决的是对象间一对多依赖关系的问题:当一个对象(被观察者 / Subject)的状态发生变化时,自动通知依赖它的多个对象(观察者 / Observer)做出响应。


一、核心思想

数据的变化与响应的行为解耦

在没有这个模式前,你可能写出这样的代码:

复制代码
user.setName("Alice"); 
logger.log("User name changed"); 
emailService.notifyAdmin("User name changed");

这让 User 对象不得不关心谁在监听它。逻辑交织、耦合紧密。

使用观察者模式后,User 不再主动调用任何服务,而是提供一种注册机制

复制代码
user.addObserver(logger); 
user.addObserver(emailService);

user.setName("Alice") 触发变化时,所有注册的观察者都会自动收到通知。


二、结构角色

观察者模式主要有四类角色:

角色 说明
Subject(抽象被观察者) 定义注册、移除、通知观察者的接口
ConcreteSubject(具体被观察者) 保存具体状态,状态变化时调用通知方法
Observer(抽象观察者) 定义接收更新的方法(如 update()
ConcreteObserver(具体观察者) 实现更新接口,根据被观察者状态做出响应

结构图:

  • 说明:把观察者和被观察者解耦,被观察者不直接依赖观察者,只通过通知接口告知状态变化。

三、简单示例

复制代码
// 观察者接口
interface Observer {
    void update(String message);
}

// 被观察者抽象类
class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer o) { observers.add(o); }
    public void removeObserver(Observer o) { observers.remove(o); }
    protected void notifyObservers(String message) {
        for (Observer o : observers) {
            o.update(message);
        }
    }
}

// 具体被观察者
class WeatherStation extends Subject {
    private String weather;

    public void setWeather(String weather) {
        this.weather = weather;
        notifyObservers("天气更新为: " + weather);
    }
}

// 具体观察者
class AppDisplay implements Observer {
    public void update(String message) { System.out.println("App显示:" + message); }
}

class EmailAlert implements Observer {
    public void update(String message) { System.out.println("邮件提醒:" + message); }
}

// 测试
public class Demo {
    public static void main(String[] args) {
        WeatherStation ws = new WeatherStation();
        ws.addObserver(new AppDisplay());
        ws.addObserver(new EmailAlert());

        ws.setWeather("多云转晴");
    }
}

四、Spring 中的观察者模式

Spring 的事件(Spring Event )机制,是 Spring 容器中对观察者模式 的一个完善实现。它提供了 发布-订阅模型(Publish-Subscribe) 的标准化支持,用于在应用内部解耦组件,让 Bean 之间通过事件进行通信。


1. Spring Event 设计思想

Spring Event 实现了观察者模式的核心结构:

角色 对应
Subject(被观察者) ApplicationContext(事件源)
Observer(观察者) ApplicationListener
Event(事件) ApplicationEvent
通知机制 ApplicationEventMulticaster

所以可以理解为:

ApplicationContext 负责发布事件,ApplicationListener 负责监听事件,ApplicationEventMulticaster 负责广播事件。


2、核心类结构
a. ApplicationEvent
  • 所有事件的基类(从 Spring 4.2 开始,也可以直接使用任意对象作为事件)。

  • 包含事件源信息(source)与时间戳。

示例:

复制代码
public class UserRegisterEvent extends ApplicationEvent {
    private final String username;

    public UserRegisterEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

b. ApplicationListener
  • 事件监听器接口,接收并处理事件。

  • 泛型指定感兴趣的事件类型。

    @Component
    public class WelcomeEmailListener implements ApplicationListener<UserRegisterEvent> {
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
    System.out.println("发送欢迎邮件给用户:" + event.getUsername());
    }
    }


c. ApplicationEventPublisher
  • 事件发布接口,ApplicationContext 实现了它。

  • 任何 Bean 都可以通过注入该接口来发布事件。

    @Service
    public class UserService {
    @Autowired
    private ApplicationEventPublisher publisher;

    复制代码
      public void registerUser(String username) {
          // 注册逻辑...
          publisher.publishEvent(new UserRegisterEvent(this, username));
      }

    }


d. ApplicationEventMulticaster
  • 事件广播器,负责查找匹配的监听器并调用。

  • 默认实现类为 SimpleApplicationEventMulticaster

  • 支持同步和异步事件分发。

    @Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
    SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
    multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); // 异步事件
    return multicaster;
    }


3、事件调用流程(核心机制)

Spring Event 的内部调用链如下:

  1. 事件发布

    • 通过 ApplicationEventPublisher.publishEvent() 发布事件。
  2. 事件广播

    • ApplicationEventMulticaster 收到事件 → 查找所有匹配的监听器。
  3. 事件分发

    • 调用每个 ApplicationListener.onApplicationEvent()

    • 默认同步执行,可配置为异步。

  4. 监听器响应

    • 监听器执行对应业务逻辑。

可用简图表示:

复制代码
UserService
    ↓
publishEvent(UserRegisterEvent)
    ↓
ApplicationContext(ApplicationEventPublisher)
    ↓
ApplicationEventMulticaster
    ↓
┌─────────────────────┬────────────────────┐
│ WelcomeEmailListener│ LogRecordListener  │
└─────────────────────┴────────────────────┘

4、异步事件处理

默认情况下,Spring 事件是同步执行的。

若要实现异步(防止阻塞主线程),有两种方式:

方式一:在 ApplicationEventMulticaster 中配置线程池
复制代码
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
public SimpleApplicationEventMulticaster asyncEventMulticaster() {
    SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
    multicaster.setTaskExecutor(Executors.newFixedThreadPool(5));
    return multicaster;
}
方式二:使用 @Async
复制代码
@Component
public class AsyncEmailListener {
    @Async
    @EventListener
    public void handle(UserRegisterEvent event) {
        System.out.println("异步发送邮件给:" + event.getUsername());
    }
}

5、基于注解的事件监听(推荐)

Spring 4.2 之后,可以用 @EventListener 代替实现接口:

复制代码
@Component
public class AnnotatedListener {
    @EventListener
    public void handleUserRegister(UserRegisterEvent event) {
        System.out.println("记录日志:新用户注册 -> " + event.getUsername());
    }
}

甚至支持条件过滤:

复制代码
@EventListener(condition = "#event.username.startsWith('admin')")
public void handleAdminRegister(UserRegisterEvent event) {
    System.out.println("管理员注册:" + event.getUsername());
}

6、应用场景

Spring Event 非常适合用于业务解耦场景:

场景 示例
用户注册 触发欢迎邮件、积分初始化、日志记录
订单支付 通知库存系统、积分系统、消息推送
系统监控 异常上报、埋点、指标采集
模块通信 不同模块之间基于事件进行低耦合通信

在微服务架构中,这个机制在单体内部就像一个"轻量版的消息队列"。


五、与其它设计模式对比

1. 观察者模式 vs. 责任链模式(Chain of Responsibility)
对比项 观察者模式 责任链模式
通知方式 一对多并行通知 一对一链式传递
执行顺序 同时通知多个观察者 顺序传递直到被处理
目的 广播事件 分级处理请求
示例 Spring Event 广播 Servlet Filter / Spring Security FilterChain

区别本质

  • 观察者是"群发通知";

  • 责任链是"层层转交"。


2. 观察者模式 vs. 中介者模式(Mediator Pattern)
对比项 观察者模式 中介者模式
结构 被观察者与观察者间通信 所有对象通过中介通信
关系 一对多 多对多(通过中心协调)
关注点 状态变化通知 复杂对象交互管理
示例 GUI 事件模型 聊天室服务器、航班调度系统

中介者是一个"事件中心 ",

而观察者模式是一个"事件传播机制"。


3. 观察者模式 vs. 命令模式(Command Pattern)
对比项 观察者模式 命令模式
意图 状态变更触发通知 封装请求为对象
调用关系 被动(事件驱动) 主动(命令调用)
适用场景 状态同步、事件响应 动作封装、可撤销操作
示例 Spring Event 监听 Spring Task、命令执行器

可以协作使用

比如一个观察者响应事件后执行具体命令(Command)。


4. 观察者模式 vs. 装饰器模式(Decorator Pattern)
对比项 观察者模式 装饰器模式
目的 对象间通信 动态扩展功能
结构 事件驱动 包装增强
时机 运行时响应 运行时功能组合
示例 监听注册事件 I/O 流的层层包装

观察者像"通知别人我变了",

而装饰器是"我穿上外衣变强了"。


六、拓展

  1. 响应式流(Reactive Stream)Observer 演化为 SubscriberSubject 变为 Publisher。它们支持背压(Backpressure),防止"观察者被淹没"。

  2. 单播 vs. 多播 :单播(Reactor 中的 Mono)是一对一的观察关系;多播(Flux)是一对多。

  3. 观察者是"事件驱动架构(EDA)"的基石。它让系统从 命令式逻辑(do A then B)转向响应式逻辑(when A happens, notify B)。

在复杂系统中,它常常与下列模式配合:

  • 工厂模式 配合,用于动态注册观察者;

  • 策略模式 配合,用于灵活决定响应策略;

  • 单例模式 配合,用于全局事件中心;

  • 命令模式 配合,用于事件触发具体动作。

相关推荐
咬_咬2 小时前
C++仿muduo库高并发服务器项目:Poller模块
服务器·开发语言·c++·epoll·muduo
⑩-2 小时前
苍穹外卖Day(1)
java·数据库·spring boot·spring·java-ee·mybatis
程序员buddha2 小时前
C语言操作符详解
java·c语言·算法
Highcharts.js2 小时前
在Python中配置高度交互的数据可视化:Highcharts完全指南
开发语言·python·信息可视化·highcharts
小生凡一2 小时前
图解|Go语言实现 Agent|LLM+MCP+RAG
开发语言·后端·golang
l0sgAi2 小时前
SpringAI 整合MCP实现联网搜索 (基于tavily)
java·后端
思茂信息2 小时前
CST电动车EMC仿真(二)——电机控制器MCU的EMC仿真
开发语言·javascript·单片机·嵌入式硬件·cst·电磁仿真
❀͜͡傀儡师2 小时前
使用DelayQueue 分布式延时队列,干掉定时任务!
java·分布式·delayqueue·spingboot