从事件机制谈起
观察者模式的事件思想
在 23 种设计模式中,观察者模式作为对象间一对多
依赖关系的实现,被广为在软件设计中使用。在观察者模式中,被观察者相当于事件中的时间发布者;而观察者相当于事件中的监听者。因此可以说,观察者模式便是事件驱动机制的一种提现。
事件驱动
事件驱动的一个常见形式便是发布-订阅
模式。在跨进程的通信间,我们通常采用引入 MQ (消息队列) 来实现消息的发布和订阅。目前主流应用的架构中,均采用消息的发布-订阅模式来进行大型分布式系统的解耦。使得数据生产方和使用方分离,同时 MQ 还可起到削峰等作用。
聊完跨进程,同一进程内很多时候也需要这种事件驱动机制来进行逻辑解耦。
试想如下场景:
现在系统中需要针对用户操作的行为进行记录,记录按照业务需求需存入缓存、MQ两处。如果此处不进行解耦直接在原有程序添加,代码如下:
以上代码示例中,我们模拟了直接在原有代码上增加记录代码后的示例。这段代码虽然能够满足需求,但是在扩展性上很差。可能会出现的问题大概有以下两个方面:
- 如果我们未来需在同项目不同的业务代码中增加新的记录时,仍需要添加同样的代码,且
doRecordCache()
和doRecordMQ()
若想进行复用,必然要将其封装到一个新的类中,但是在这种场景下封装一个新的类是没有语义的。 - 如果未来该记录需要存储到第三个地方,则需要改动每个代码,实现新方式的增加,这显然是不够理想的。
在这种情况下,使用进程内的事件发布-订阅机制便可以大大增加该场景下代码的复用性和可读性。
使用事件机制
事件机制主要由三个部分组成:事件源,事件对象,监听器,具体描述如下:
- 事件源:事件发生的起源
- 事件对象:事件实体,事件对象会持有一个事件源
- 监听器:监听事件对象,对事件对象进行处理
Java 提供的事件机制
Java 提供了事件相关的接口定义,具体包含以下两个:
- EventObject: 事件对象,自定义事件对象需要继承该类
- EventListener: 事件监听器接口
由于事件源 Source 不需要实现任何接口,所以 Java 中没给出相应的定义。
一个简单的利用 Java 原生实现事件模型的例子:
|
|
以上代码示例便是在 Java 中实现一个事件的发布与监听的过程。
Spring 事件使用
Spring 提供了事件相关的类和接口,在 Spring 项目中可以通过实现提供的接口,来实现事件的发布-订阅。Spring 的事件机制以 Java 的事件机制作为基础,按需进行了扩展。
Spring 中与事件相关的定义如下:
- ApplicationEvent:继承 EventObject 类,事件源应继承该类;
- ApplicationEventListener
:事件监听者,该类接收一个泛型,供 ApplicationEventPublisher 在发布事件时选择 EventListener; - ApplicationEventPublisher:封装发布事件的方法,通知所有在 Spring 中注册的该事件的监听者进行处理;
- ApplicationEventPublisherAware:Spring 提供的 Aware 接口之一,实现该接口的 Bean 可以获取 ApplicationEventPublisher 并进行发布事件。
一个简单利用 Spring 事件机制进行事件发布-订阅的例子
以上代码中,可以看到。在 Spring 框架使用事件与在 Java 中使用时间机制其实并没有什么不同,均由 事件源、事件对象以及事件监听者组成。与 Java 原生提供的事件机制不同的是,Spring 中提供了 ApplicationEvent
类作为基类,开发者可以以此为基础定义自己的自定义事件。
在 Spring 中,继承自 ApplicationEvent 的事件对象的监听者,可以由 Spring 容器进行管理,并在发布时通过 ApplicationEventPublisher 进行发布。这就避免了我们自己实现监听者的注册和通知过程,免去了很多繁杂的过程,使得更专心于业务本身。
总结
本文主要介绍了进程内如何通过发布-订阅模式进行解耦,针对于 Spring 中的事件框架,还应有很多更深层次的挖掘,包括 ApplicationEventPublisher 的原理,以及事件处理中的一些多线程问题。这些问题将在下篇博文中进行深入探寻,了解 Spring 的设计奥妙。