Java观察者模式3种实现深度对比:从原生接口到Spring Event的工程实践

1. 观察者模式核心价值与应用场景

在软件系统中,对象间的状态依赖关系常常是复杂多变的。当订单状态变更需要通知物流系统、库存系统和用户端时,硬编码的调用方式会让系统耦合度急剧上升。观察者模式通过解耦主题对象与观察者,让状态变化能够自动传播到相关对象,这正是其核心价值所在。

典型应用场景包括

  • 电商订单状态变更通知(支付成功、发货、签收等)
  • 配置中心参数更新时的多系统同步
  • 物联网设备状态监控与告警
  • GUI事件处理机制(如按钮点击事件)

以订单系统为例,传统实现可能直接在订单状态变更方法中调用库存扣减、物流创建等方法。这种实现存在三个明显问题:

  1. 违反开闭原则,新增观察者需要修改主题类代码
  2. 调用链脆弱,任一观察者异常会影响整体流程
  3. 难以实现异步通知机制

观察者模式通过抽象解耦,完美解决了这些问题。下面我们通过具体代码示例,对比Java中三种典型的实现方式。

2. 原生JDK实现:java.util.Observable

Java标准库早在1.0版本就提供了观察者模式的实现基础,虽然现在已被标记为 @Deprecated ,但理解其设计仍有重要意义。

2.1 基础实现示例

// 被观察者(订单)
class OrderObservable extends Observable {
    private String orderStatus;
    
    public void setStatus(String newStatus) {
        this.orderStatus = newStatus;
        setChanged();  // 标记状态已变更
        notifyObservers(newStatus);  // 通知所有观察者
    }
}

// 观察者(库存系统)
class InventoryObserver implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("库存系统收到订单状态更新: " + arg);
        // 实际业务逻辑:库存扣减、预留等
    }
}

// 使用示例
public class JdkObserverDemo {
    public static void main(String[] args) {
        OrderObservable order = new OrderObservable();
        order.addObserver(new InventoryObserver());
        order.addObserver(new LogisticsObserver());
        
        order.setStatus("PAID");  // 支付成功状态变更
    }
}

2.2 优缺点分析

优势

  • 开箱即用,无需额外依赖
  • 线程安全的通知机制( notifyObservers 方法同步)
  • 提供 setChanged() 方法控制通知条件

缺陷

  • Observable 是类而非接口,限制了复用
  • 缺乏泛型支持,需要类型转换
  • 通知顺序不可控
  • Java 9后被标记为废弃

提示:在Java 9+环境中,建议使用 PropertyChangeListener 替代,或选择其他实现方式

3. 自定义实现:灵活可控的观察者模式

当需要更精细控制时,自定义实现是更好的选择。下面展示一个支持泛型、事件过滤的增强版实现。

3.1 完整代码实现

// 事件对象基类
abstract class Event {
    private long timestamp = System.currentTimeMillis();
    // 可扩展公共事件属性
}

// 订单状态事件
class OrderStatusEvent extends Event {
    private String orderId;
    private String newStatus;
    private String oldStatus;
    
    // 构造器、getters省略
}

// 观察者接口
interface CustomObserver<T extends Event> {
    void onEvent(T event);
    default Predicate<T> eventFilter() {
        return e -> true;  // 默认接收所有事件
    }
}

// 被观察者基类
class EventPublisher<T extends Event> {
    private final List<CustomObserver<? super T>> observers = new CopyOnWriteArrayList<>();
    
    public void register(CustomObserver<? super T> observer) {
        observers.add(observer);
    }
    
    protected void notifyObservers(T event) {
        observers.stream()
            .filter(o -> o.eventFilter().test(event))
            .forEach(o -> o.onEvent(event));
    }
}

// 具体订单被观察者
class OrderPublisher extends EventPublisher<OrderStatusEvent> {
    public void statusChanged(String orderId, String oldStatus, String newStatus) {
        OrderStatusEvent event = new OrderStatusEvent(orderId, oldStatus, newStatus);
        notifyObservers(event);
    }
}

// 使用示例
class InventoryService implements CustomObserver<OrderStatusEvent> {
    @Override
    public void onEvent(OrderStatusEvent event) {
        if ("SHIPPED".equals(event.getNewStatus())) {
            System.out.println("库存系统: 订单已发货,释放预留库存");
        }
    }
    
    @Override
    public Predicate<OrderStatusEvent> eventFilter() {
        return e -> !e.getNewStatus().equals(e.getOldStatus());
    }
}

3.2 进阶特性对比

特性 JDK Observable 自定义实现
类型安全 ✅ 泛型支持
事件过滤 ✅ 灵活配置
异步通知 ✅ 可自由实现
观察者优先级 ✅ 可排序实现
生命周期管理 简单 完善
性能开销 中等(需权衡特性)

工程实践建议

  • 简单场景可使用 PropertyChangeSupport
  • 复杂事件系统推荐自定义实现
  • 高频事件考虑引入事件总线(如Guava EventBus)

4. Spring Event:企业级解决方案

在Spring生态中,应用上下文的事件机制提供了开箱即用的观察者模式实现,特别适合分布式系统。

4.1 基本用法

// 定义事件(支持序列化)
public class OrderEvent extends ApplicationEvent {
    private final String orderId;
    private final String eventType;
    
    public OrderEvent(Object source, String orderId, String eventType) {
        super(source);
        this.orderId = orderId;
        this.eventType = eventType;
    }
    // getters...
}

// 监听器实现
@Component
class OrderEventListener {
    @Async  // 支持异步处理
    @EventListener(condition = "#event.eventType == 'PAY_SUCCESS'")
    public void handlePaymentSuccess(OrderEvent event) {
        // 处理支付成功逻辑
    }
    
    @TransactionalEventListener(phase = AFTER_COMMIT)
    public void handleAfterCommit(OrderEvent event) {
        // 事务提交后处理
    }
}

// 事件发布
@Service
class OrderService {
    private final ApplicationEventPublisher publisher;
    
    public void payOrder(String orderId) {
        // 业务处理...
        publisher.publishEvent(new OrderEvent(this, orderId, "PAY_SUCCESS"));
    }
}

4.3 Spring Event高级特性

  1. 事务绑定事件

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void onOrderCompleted(OrderEvent event) {
        // 只在事务成功提交后执行
    }
    
  2. 条件过滤

    @EventListener(condition = "#event.eventType matches '^PAY_.*'")
    
  3. 异步处理

    @Async
    @EventListener
    public void asyncHandle(OrderEvent event) {
        // 异步执行
    }
    
  4. 响应式支持

    @EventListener
    public Mono<Void> reactiveHandle(OrderEvent event) {
        return Mono.fromRunnable(() -> {
            // 响应式处理
        });
    }
    

5. 三种实现对比与选型指南

5.1 功能对比表

特性 JDK Observable 自定义实现 Spring Event
学习成本
与框架集成 完美
事务支持
异步支持 手动实现
分布式场景适用性 有限
性能开销 中高
扩展灵活性

5.2 选型决策树

graph TD
    A[需要框架集成?] -->|是| B[Spring Event]
    A -->|否| C{需要高级特性?}
    C -->|是| D[自定义实现]
    C -->|否| E[JDK Observable/PropertyChangeSupport]

实际项目经验

  • 单体应用:Spring Event + 自定义简单事件
  • 微服务架构:Spring Cloud Bus + 消息队列
  • 高频事件:自定义实现 + Disruptor框架
  • 跨语言系统:Apache Kafka事件总线

6. 性能优化与常见陷阱

6.1 性能优化点

  1. 观察者注册管理

    // 使用CopyOnWriteArrayList替代普通ArrayList
    private final List<Observer> observers = new CopyOnWriteArrayList<>();
    
  2. 事件过滤前置

    // 在通知前进行快速过滤
    if (needsNotification(event)) {
        notifyObservers(event);
    }
    
  3. 异步处理模式

    executor.submit(() -> observers.forEach(o -> o.onEvent(event)));
    

6.2 常见问题与解决方案

问题1:内存泄漏

  • 原因:观察者未正确注销
  • 方案:使用弱引用或确保注销
    private final Map<Observer, WeakReference<Observer>> observers = new ConcurrentHashMap<>();
    

问题2:通知顺序依赖

  • 方案:引入优先级机制
    observers.sort(Comparator.comparingInt(Observer::getPriority));
    

问题3:异常传播

  • 方案:隔离处理每个观察者
    observers.forEach(o -> {
        try {
            o.update();
        } catch (Exception e) {
            log.error("Observer failed", e);
        }
    });
    

在电商系统订单模块的实践中,我们最终采用了Spring Event为主、自定义实现为辅的混合方案。核心状态变更使用同步的Spring Event保证事务一致性,非关键业务(如数据统计、日志记录)使用自定义的异步观察者实现。这种组合既保证了核心流程的可靠性,又提升了系统整体吞吐量。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐