Java观察者模式事件通知实现全解析:从原理到实战
目录导读
观察者模式核心概念
问答环节
Q:观察者模式解决了什么问题?
A:解决对象间一对多的依赖关系——当一个对象状态变化时,所有依赖它的对象自动收到通知并更新,典型场景包括:GUI事件监听、消息推送、分布式系统状态同步。

模式三要素
- 主体(Subject):维护观察者列表,提供注册/删除接口
- 观察者(Observer):定义更新接口
- 具体实现:Event(事件对象)作为通知载体
设计原则
遵循开放封闭原则(对扩展开放,对修改封闭)和依赖倒置原则(面向接口编程)。
Java原生实现方式
1 基于java.util.Observable和Observer
// 被观察者
public class WeatherData extends Observable {
private float temperature;
public void setTemperature(float temp) {
this.temperature = temp;
setChanged(); // 标记状态已改变
notifyObservers(temp); // 推送数据
}
}
// 观察者
public class DisplayBoard implements Observer {
@Override
public void update(Observable o, Object arg) {
float temp = (float) arg;
System.out.println("温度更新至:" + temp);
}
}
注意:Java 9后该类标记为Deprecated,推荐使用PropertyChangeListener。
2 自定义纯接口实现
// 事件类
public class TemperatureEvent {
private final float value;
// 构造器、getter...
}
// 观察者接口
public interface TemperatureObserver {
void onTemperatureChange(TemperatureEvent event);
}
// 可观察者接口
public interface Observable {
void addObserver(TemperatureObserver o);
void removeObserver(TemperatureObserver o);
void notifyObservers(TemperatureEvent e);
}
优势:完全控制实现细节,避免JDK内置类的线程安全问题。
基于事件监听的进阶实现
1 使用EventObject + EventListener
// 定义事件
public class DataChangeEvent extends EventObject {
private final Object newData;
public DataChangeEvent(Object source, Object newData) {
super(source);
this.newData = newData;
}
}
// 定义监听器接口
public interface DataChangeListener extends EventListener {
void onDataChanged(DataChangeEvent event);
}
// 实现事件源
public class DataSource {
private final List<DataChangeListener> listeners = new ArrayList<>();
public void addListener(DataChangeListener listener) {
listeners.add(listener);
}
public void updateData(Object data) {
// 业务逻辑
fireEvent(new DataChangeEvent(this, data));
}
private void fireEvent(DataChangeEvent event) {
listeners.forEach(l -> l.onDataChanged(event));
}
}
2 异步通知实现
// 使用线程池异步通知
private final ExecutorService executor = Executors.newCachedThreadPool();
private void fireEventAsync(DataChangeEvent event) {
listeners.forEach(l -> executor.submit(() -> l.onDataChanged(event)));
}
性能要点:同步通知可能阻塞主流程,异步需注意线程安全。
Spring框架中的事件机制
1 Spring事件三要素
- Event:继承ApplicationEvent
- Publisher:注入ApplicationEventPublisher
- Listener:使用@EventListener注解
// 事件
public class OrderCreatedEvent extends ApplicationEvent {
private final Long orderId;
public OrderCreatedEvent(Object source, Long orderId) {
super(source);
this.orderId = orderId;
}
}
// 发布
@Component
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder() {
// 业务逻辑
publisher.publishEvent(new OrderCreatedEvent(this, 1001L));
}
}
// 监听
@Component
public class NotificationListener {
@EventListener
@Async // 异步执行
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("订单" + event.getOrderId() + "已创建,发送通知");
}
}
2 Transactional事件监听
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(OrderCreatedEvent event) {
// 事务提交后执行
}
优势:与Spring事务集成,避免脏数据问题。
性能优化与常见陷阱
1 性能优化策略
- 弱引用观察者:避免内存泄漏
private final List<WeakReference<Observer>> observers = new ArrayList<>();
- 批量通知:合并高频事件
- 事件过滤:使用topic/type字段区分事件类型
2 常见陷阱
- 循环通知:A监听B,B又监听A,导致无限递归
解决方案:使用事件序号或状态检查 - 观察者抛出异常:导致后续观察者无法收到通知
解决方案:捕获异常继续传播 - 线程安全问题:多线程同时注册/删除观察者
解决方案:使用CopyOnWriteArrayList或synchronized块
FAQ:高频问题解答
Q1:观察者模式和发布订阅模式有什么区别?
A:观察者模式是直接通信(Subject维护Observer列表),发布订阅模式通过事件通道解耦(Publisher和Subscriber不直接感知),RabbitMQ/Kafka属于发布订阅。
Q2:JDK内置观察者模式为什么被废弃?
A:Observable是类而非接口,违反单一职责;序列化支持差;事件通知缺少泛型支持,推荐使用PropertyChangeSupport或自定义接口。
Q3:Spring @EventListener和@TransactionalEventListener有何不同?
A:后者绑定事务生命周期,可指定在提交前/后、回滚后等阶段执行,防止事务未提交时访问数据库造成的不一致性。
Q4:如何实现观察者优先级?
A:使用有序集合如PriorityBlockingQueue,或为观察者标记order属性,通知前排序。
Q5:生产环境中如何监控事件通知链路?
A:使用AOP切面记录事件发布/处理的耗时,结合Micrometer指标暴露,搭配Prometheus监控,或使用分布式追踪如Zipkin。
Java观察者模式的事件通知实现从原生JDK到Spring框架,已形成完整生态,核心在于解耦与扩展性,实际开发中建议优先选择Spring事件机制(集成事务、异步支持),对于轻量级场景采用自定义接口实现,注意线程安全和内存管理,才能构建健壮的事件驱动系统。