上篇请看 从观察者模式到Java事件处理机制(上)
三、事件模型
3.1、基本概念
事件模型也算是一种设计思想,不过,它并不是 23 种设计模式之一,相对来说,事件模型应该算是观察者模式的一种变种/升级,为什么这么说呢?
事件模型有三个重要的角色,分别是事件源(Source)、事件(EventObject)、事件监听器(EventListener),事件源表示事件发生的源头,事件表示发生的具体事件对象,事件监听器则监听发生的事件并做出处理。这三者之间的关系如下图所示:
大致来说,每个事件源可以产生若干个事件,而每个事件源可以添加若干个监听器,监听器可以监听特定的事件,当监听器监听到特定的事件发生时,就可以调用监听器中相应的方法来处理对应的业务逻辑。
相对观察者模式来说,事件模型中的事件源大致可认为是被观察者,事件监听器可大致认为是观察者。以裁判员、运动员的场景为例,在事件模型下,裁判员可以认为是事件源,运动员可以认为是监听器(监听者),而裁判员打响发令枪则可以认为裁判员产生了一个”发令起跑“事件。这么一类比,是不是和观察者模式很像呢?
Java 为方便实现事件模型,JDK 也提供了两个基本类,一个是java.util.EventListener
接口、一个是java.util.EventObject
类,源代码如下:
1、事件监听器接口EventListener
:
package java.util;
/**
* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}
复制代码
2、事件对象EventObject
:
package java.util;
/**
* <p>
* The root class from which all event state objects shall be derived.
* <p>
* All Events are constructed with a reference to the object, the "source",
* that is logically deemed to be the object upon which the Event in question
* initially occurred upon.
*
* @since JDK1.1
*/
public class EventObject implements java.io.Serializable {
private static final long serialVersionUID = 5516075349620653480L;
/**
* The object on which the Event initially occurred.
*/
protected transient Object source;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @exception IllegalArgumentException if source is null.
*/
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
/**
* The object on which the Event initially occurred.
*
* @return The object on which the Event initially occurred.
*/
public Object getSource() {
return source;
}
/**
* Returns a String representation of this EventObject.
*
* @return A a String representation of this EventObject.
*/
public String toString() {
return getClass().getName() + "[source=" + source + "]";
}
}
复制代码
从 JDK 源码不难看出,JDK 对于事件模型进行了最高级的抽象,基本就是标记一下"我是一个监听器,我是一个事件对象",而对于事件源连抽象都没有提供(EventListener 空接口只是起到标识的作用,标记具体实现对象是一个监听器/监听者)。
是的,Java 的提供的事件机制就是如此简洁!大家可以对比看一下 JDK 为观察者模式提供的 Observer
、Observable
这两个类的源码,体会一下这两者之间的区别。(JDK 中自带的事件实现类,更多的是面对 GUI 场景下的java.awt.event
包下的相关事件类,如 MouseEvent, KeyEvent 等。事件机制或者说事件驱动机制,这个提法在有交互的业务场景下可能更好理解一些,比方说用户点击了一个按钮事件,打开了一个页面事件什么的,但是呢,只要理解了事件机制的思维方式,就可以灵活地自定义各种事件,更好地实现特定地场景需求)
3.2、应用示例
接下来,我们试着用 Java 事件机制的方式来实现一下裁判员、运动员的例子,以进一步对比体会在 Java 中,观察者模式、事件机制实现同一业务场景的差异。
初步分析一下,在事件机制下,接口/类该如何设计呢?首先,需要定义一个”发令起跑“的事件对象,其次,作为事件源的裁判员,要能够发布事件、添加/删除监听器,而作为监听器的运动员要能够处理监听到的事件,所以,类图可设计如下图所示:
为了更好的对比,笔者定义了一个空的事件对象StartCommandEvent
(实际应用中,事件对象中通常会包含一些事件所特有的一些属性/方法,这也是事件模型的关键所在),同时,定义了一个StartCommandListener
接口,在接口中定义了具体监听器(运动员)需要实现自己业务逻辑的 update()
方法,具体代码实现如下:
1、事件源 裁判员对象Referee
:
import java.util.EventListener;
import java.util.Vector;
/**
* 裁判员 (事件源)
*
* @author : laonong
*/
public class Referee {
/**
* 裁判员名称
*/
private String name;
public String getName() {
return name;
}
/**
* 监听器集合
*/
private Vector<EventListener> listeners;
public Referee(String name) {
this.name = name;
this.listeners = new Vector<>();
}
/**
* 添加监听器
* @param listener
*/
public synchronized void addListener(EventListener listener){
listeners.addElement(listener);
}
/**
* 删除监听器
* @param listener
*/
public synchronized void delListener(EventListener listener){
listeners.removeElement(listener);
}
/**
* 发布事件
* @param args
*/
public void startCommand(Object args){
System.out.println("裁判员" + this.name+ "说:" + args);
//发令起跑事件
StartCommandEvent startCommandEvent = new StartCommandEvent(this);
for(EventListener listener : listeners){
((Sportsman)listener).update(startCommandEvent,args);
}
}
}
复制代码
2、 事件对象StartCommandEvent
:
import java.util.EventObject;
/**
* 发令起跑 事件
*
* @author : laonong
*/
public class StartCommandEvent extends EventObject {
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public StartCommandEvent(Object source) {
super(source);
}
}
复制代码
3、 自定义”发令起跑“事件监听器接口StartCommandListener
:
import java.util.EventListener;
/**
* 发令起跑事件 监听接口
*
* @author : laonong
*/
public interface StartCommandListener extends EventListener {
/**
* 发令起跑事件 调用
* @param startCommandEvent
* @param args
*/
void update(StartCommandEvent startCommandEvent, Object args);
}
复制代码
4、事件监听器实现 运动员对象Sportsman
:
/**
* 运动员 (事件监听器)
*
* @author : laonong
*/
public class Sportsman implements StartCommandListener {
/**
* 运动员名字
*/
private String name;
public String getName() {
return name;
}
public Sportsman(String name) {
this.name = name;
}
/**
* 监听到事件,开始跑
* @param startCommandEvent
* @param args
*/
@Override
public void update(StartCommandEvent startCommandEvent, Object args) {
Referee referee = (Referee) startCommandEvent.getSource();
System.out.println("运动员 " + this.name + " 监听到(听到)裁判员" + referee.getName() + "的发令起跑事件:" + args + "。" + this.name + "开始跑。");
}
}
复制代码
以上,我们就完成了利用 Java 的事件机制实现裁判员、运动员的业务场景代码,接下来我们看下调用过程:
/**
* 事件模型 执行模拟
*
* @author : laonong
*/
public class Client {
public static void main(String[] args) {
//1、裁判员 (事件源)
Referee referee = new Referee("小明");
//2、运动员 (监听器 【监听者】)
Sportsman daqiang = new Sportsman("大强");
Sportsman dazhuang = new Sportsman("大壮");
//3、给事件源(裁判员)添加监听器(运动员)
referee.addListener(daqiang);
referee.addListener(dazhuang);
//4、事件源产生事件 (裁判员发令起跑事件)
referee.startCommand("大家开始跑啊");
}
}
复制代码
对比一下前面观察者模式下的实现/调用代码,对比思考一下,代码看起来是不是很像!“像”就对了,正是因为“像”,所以才说事件模型是观察者模式的一种变种。那么,可能就会有疑问了,既然这么像,为什么还要有事件模型呢?它的意义是什么,或者说和观察者模式有什么区别呢?
3.3、观察者模式、事件模型的区别
初看起来,观察者模式、事件模型这两者之间确实好像没什么区别,但是,细想一下,还是有一些不同,其中,笔者以为主要的差异有以下这些方面:
1、观察者模式只有被观察者、观察者两个主要的角色,而事件模型有事件源、事件、事件监听器三个主要的角色,事件模型进一步降低了角色之间的依赖耦合,增强了使用的灵活性,但是,同时也增大了系统整体的复杂性。(一般来说,用观察者模式能处理的业务场景,用事件模型也能够较好地处理,反过来则不一定。观察者模式一般更适用于一个对象只有一种行为的场景,而如果说一个对象有多种行为,则用事件模型处理会更合适,对象的一个特定行为对应一个监听器去处理,这当然会增加大量的监听器代码类,但比观察者模式下写大量if
/else
判断要逻辑更清晰一些,扩展性也更好。)
2、事件模型中,传递信息的是事件对象,在事件对象中可以封装明确的事件状态、属性方法等,通过具体事件对象的引入可以更明确地在事件源、监听器之间传递信息,而观察者模式下传递的信息则是 Object 对象,不够明确。
3、观察者模式下,被观察者需继承 Observable 类,由父类维护被观察者列表,而事件模型下,则是事件源自己维护监听器列表,不需继承父类,观察者模式处理的方式确实带来了使用的便利,但同时也意味着束缚,如果说被观察者对象本身有其它的父类的话,由于 Java 是单继承的,这种情况下通过观察者模式来实现就会有些不方便了。
4、观察消息、事件,在场景语义上有所不同,从概念语义上来说,有些业务场景用消息描述合适一些,比方说发布/订阅场景下的发布消息、订阅消息,有些业务场景用事件来描述会更贴合、更好理解一些,比方说,点击按钮事件、订单创建事件等等。
四、应用场景实践
为了让大家对事件机制的实际应用场景有更进一步的理解,接下来,笔者通过一个常见的实际案例来简单演示一下事件机制在日常工作中的应用。
4.1、场景描述
订单模块是常见的、重要的系统组成部分,而订单支付成功是订单状态扭转过程中的一个关键节点,在订单支付成功后,我们需要更新数据库中订单的状态为已支付。
这个业务场景下,一般伪代码可实现如下:
首先,有一个订单服务接口:
/**
* 订单服务接口
*
* @author : laonong
*/
public interface OrderService {
/**
* 订单支付成功处理方法
* @param orderBO
* @return String
*/
String paySuccessCallback(OrderBO orderBO);
}
复制代码
其次,订单服务实现类:
/**
* 订单服务实现类
*
* @author : laonong
*/
@Service
public class OrderServiceImpl implements OrderService {
@Override
public String paySuccessCallback(OrderBO orderBO) {
//更新订单的状态
System.out.println("更新DB订单状态为支付成功");
return "S";
}
}
复制代码
以上就是模拟订单支付成功后,回调订单服务方法的最基本操作,即“更新订单的状态”,业务逻辑非常地简洁、清晰。但是呢,在实际业务场景中,作为订单状态扭转的关键节点,订单支付成功后,通常还会有很多依赖于该节点的关联事情需要做,比方说,更新订单使用的优惠劵状态,给用户发送相应的订单支付成功站内消息等等,此时 paySuccessCallback()
方法实现代码可调整如下所示:
/**
* 订单支付成功处理方法
* @param orderBO
* @return String
*/
@Override
public String paySuccessCallback(OrderBO orderBO) {
//更新订单的状态
System.out.println("更新DB订单状态为支付成功");
//更新订单使用的优惠劵状态
updateOrderCoupon(orderBO);
//发送支付成功的站内消息通知
sendPaySuccessMsg(orderBO);
return "S";
}
private void updateOrderCoupon(OrderBO orderBO) {
//TODO 模拟更新订单使用的优惠券状态逻辑
System.out.println(String.format("更新DB中订单使用的优惠劵状态,orderId:%s",orderBO.getOrderId()));
}
private void sendPaySuccessMsg(OrderBO orderBO) {
//TODO 模拟发送站内消息逻辑
System.out.println(String.format("发送支付成功的站内消息通知,orderId:%s",orderBO.getOrderId()));
}
复制代码
经过这样的调整,我们确实可以满足我们的业务需求,但是呢,实际业务需求是不断变化调整的。比方说,如果后续需要在订单支付成功后,给用户添加会员积分、给满足条件订单的用户额外赠送优惠券、给销售榜单服务上报订单数据等等,如果要满足这些不断变化且越来越复杂的业务需求,就不得不不断地修改 paySuccessCallback()
方法!此时,我们会发现 paySuccessCallback()
方法越来越“膨胀”,开发/维护起来越来越麻烦,也不符合代码开发中的开闭原则。
4.2、事件机制实现改造
接下来,我们利用事件机制来实现一下这个业务场景。
简单分析一下,在以上订单业务场景中,最关键的点是什么?当然是“更新订单状态”,在这一步操作成功后,我们才会进行其它依赖步骤的操作,因此,我们可以定义一个“订单支付成功事件”,在更新订单状态成功后,发布这一事件,其它依赖的服务监听“订单支付成功事件”,在收到发布的事件通知后,再执行自己的业务操作。
由于 Spring
框架在 Java 实际应用中的普遍性,笔者此处演示代码也是基于 springboot(相应 Spring 版本为 5.3.16)。
1、定义一个“订单支付成功事件对象”:
import cn.dgchong.webdemo.model.OrderBO;
import org.springframework.context.ApplicationEvent;
/**
* 订单支付成功事件对象
*
* @author : laonong
*/
public class OrderPaySuccessEvent extends ApplicationEvent {
/**
* 订单信息BO ,封装订单的一些基本信息
*/
private OrderBO orderBO;
public OrderPaySuccessEvent(Object source, OrderBO orderBO) {
super(source);
this.orderBO = orderBO;
}
public OrderBO getOrderBO() {
return orderBO;
}
}
复制代码
2、在订单服务实现类 OrderServiceImpl
中添加事件发布器,并发布事件:
import cn.dgchong.webdemo.model.OrderBO;
import cn.dgchong.webdemo.service.OrderService;
import cn.dgchong.webdemo.service.event.OrderPaySuccessEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* 订单服务实现类
*
* @author : laonong
*/
@Service
public class OrderServiceImpl implements OrderService {
/**
* 事件发布器
*/
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
/**
* 订单支付成功处理方法
* @param orderBO
* @return String
*/
@Override
public String paySuccessCallback(OrderBO orderBO) {
//更新订单的状态
System.out.println("更新DB订单状态为支付成功");
/**
* 更新DB订单状态为支付成功后 , 发布订单支付成功事件
*/
applicationEventPublisher.publishEvent(new OrderPaySuccessEvent(this,orderBO));
return "S";
}
}
复制代码
3、定义优惠券事件监听器,用于监听处理订单支付成功事件:
import cn.dgchong.webdemo.model.OrderBO;
import cn.dgchong.webdemo.service.event.OrderPaySuccessEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 优惠券 订单支付成功事件监听器
*
* @author : laonong
*/
@Component
public class CouponOrderPaySuccessListener implements ApplicationListener<OrderPaySuccessEvent> {
/**
* 监听订单支付成功事件,事件发布后会回调该方法,在这个方法里就可以完成自己特定的业务逻辑
* @param event
*/
@Override
public void onApplicationEvent(OrderPaySuccessEvent event) {
//TODO 监听器处理 模拟更新订单使用的优惠券状态逻辑
OrderBO orderBO = event.getOrderBO();
System.out.println(String.format("更新DB中订单使用的优惠劵状态,orderId:%s",orderBO.getOrderId()));
}
}
复制代码
通过以上三步,我们就利用事件机制完成了“订单支付成功”业务场景的改造,非常的简单、快速!可以用以下代码调用服务测试验证一下:
/**
* 订单支付成功处理 测试
*/
@Test
public void paySuccessCallbackTest(){
OrderBO orderBO = new OrderBO();
orderBO.setOrderId(1001L);
orderService.paySuccessCallback(orderBO);
}
复制代码
可以发现,服务调用方的调用不需做任何改变,但我们利用事件机制实现了相同的最终效果,而后续,如果说对“订单支付成功”业务需要扩展也变得非常方便,比方说,订单支付成功后需要给用户发站内消息,不再需要修改 OrderServiceImpl
服务中的任何代码,只需要增加一个对应的站内消息监听器即可!示例代码如下:
import cn.dgchong.webdemo.model.OrderBO;
import cn.dgchong.webdemo.service.event.OrderPaySuccessEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 优惠券 订单支付成功事件监听器
*
* @author : laonong
*/
@Component
public class CouponOrderPaySuccessListener implements ApplicationListener<OrderPaySuccessEvent> {
/**
* 监听订单支付成功事件 回调方法
* @param event
*/
@Override
public void onApplicationEvent(OrderPaySuccessEvent event) {
//TODO 监听器处理 模拟更新订单使用的优惠券状态逻辑
OrderBO orderBO = event.getOrderBO();
System.out.println(String.format("事件监听器处理,更新DB中订单使用的优惠劵状态,orderId:%s",orderBO.getOrderId()));
}
}
复制代码
代码说明:
通过以上示例代码可知,只需要创建事件对象、添加事件发布器发布事件,添加监听器处理事件这简单的三步,就完成了基于事件机制实现业务场景的改造,简单且高效!而之所以能够做到这点,是因为充分利用了 Spring
框架的强大能力。
在演示代码中,出现了 3 个非常重要的类,分别是 ApplicationEvent
、ApplicationListener
、ApplicationEventPublisher
,这其中, ApplicationEvent
类是 Spring 扩展了 JDK 中的 EventObject
类,ApplicationListener
类是 Spring 扩展了 JDK 中的 EventListener
类,而 ApplicationEventPublisher
则是 Spring 框架定义的事件发布器接口,用来发布事件。这三个类/接口的源代码都并不复杂,笔者就不再赘述。
当然,这 3 个类只是 Spring
框架为了方便用户使用事件机制而提供的“标识入口”而已,如果只是使用的话,只需要知道Spring
框架在容器启动时,就会将事件发布器注入到 ApplicationContext
上下文中,而只要实现了ApplicationListener
监听器接口的 Bean 都会被扫描到容器中(通过@Component
注解扫描,如果删除该注解,就不会被扫到了),在监听到发布 ApplicationEvent
事件后,就会回调其中的 onApplicationEvent()
方法。( Spring
框架本身在背后是做了很多工作的,让用户只专注于自己的具体业务需求即可,这其中的原理不是一两句话就可以说清楚的,也不是本文重点,笔者也就不再赘述了,感兴趣的同学可以去了解一下。)
tips:
实际业务场景下,尤其是大型分布式系统,面对“订单支付成功”这样的核心链路业务场景,为了进一步提升系统的处理能力与系统的可靠性,更多地会选择引入消息中间件来处理问题,当然,这又是另一个庞大的话题了。
4.3、Spring 事件机制其它说明
强大的Spring
框架提供了很多便利的方法/注解,以帮助我们充分利用事件机制处理遇到的实际问题,笔者此处对于常见的应用场景下的使用,进行简单说明。
1、支持异步模式
Spring 的事件机制默认是同步阻塞的,在事件发布器 ApplicationEventPublisher
发布事件后,主线程会一直阻塞等待监听器的响应,如果说有多个监听器要执行,或者出现某个监听器业务执行较慢的话,则发布事件的主业务方法就会一直阻塞等待。有时这可能并不是我们想要的结果,此时我们可以通过简单的配置,就可以让监听器实现异步。
场景一:只将某一个监听器改成异步执行。
这种情况下,只需要在监听器的回调方法onApplicationEvent()
上加上 @Async
注解就可以了。(启动类上需要添加 @EnableAsync
注解开启异步调用,但此时异步用的是 Spring 默认的线程池,实际工作中建议使用自定义线程池)
场景二:所有监听器默认都改成异步执行。
这种情况下,我们需要添加一个配置类,然后在配置类中自定义线程池,并将自定义线程池设置到自定定义事件广播器中。以下为演示代码:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
import java.util.concurrent.Executor;
/**
* bean config 类
*
* @author : laonong
*/
@Configuration
@ComponentScan
public class BeanConfig {
/**
* 自定义 事件广播器
* @return
*/
@Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
//创建一个事件广播器
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
//给广播器设置一个线程池,就可以通过线程池中的线程,来异步调用事件监听器
Executor executor = this.applicationEventMulticasterThreadPool().getObject();
//设置线程池
eventMulticaster.setTaskExecutor(executor);
return eventMulticaster;
}
/**
* 自定义 线程池
* @return
*/
@Bean
public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
ThreadPoolExecutorFactoryBean threadPool = new ThreadPoolExecutorFactoryBean();
threadPool.setThreadNamePrefix("applicationEventMulticasterThreadPool-");
threadPool.setCorePoolSize(5);
threadPool.setMaxPoolSize(10);
threadPool.setKeepAliveSeconds(60);
threadPool.setQueueCapacity(100);
threadPool.setThreadNamePrefix("event-listener-thread-");
return threadPool;
}
}
复制代码
tips:
从这里可知,有的地方说引入事件机制后,后续扩展监听器代码时,因为不需修改原主业务方法中的代码,所以不用担心主业务方法受到影响,这是不严谨的。其实,即便是改成异步的,也还是可能会有影响的,因为主业务线程、监听器异步线程还在一个 JVM 中,所以,一旦有调整,还是需要充分考虑/测试对系统整体的影响。
2、支持监听器执行顺序控制
Spring
中监听器的执行顺序,默认是按照监听器 Bean 自然装载的顺序执行的,如果要指定监听器的执行顺序的话,只需要在监听器 Bean 上添加 @Order
注解即可(数值越小,执行顺序越高)。
3、支持注解方式定义监听器
笔者演示代码中的监听器是通过实现 ApplicationListener
接口实现的,Spring 还提供了@EventListener
注解用于快速定义一个监听器。比方说,如果我们需要添加一个会员积分处理的监听器,还可以按以下演示代码来实现:
import cn.dgchong.webdemo.model.OrderBO;
import cn.dgchong.webdemo.service.event.OrderPaySuccessEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* 会员积分服务 订单支付成功事件监听器
*
* @author : laonong
*/
@Component
public class MemberPointsOrderPaySuccessListener {
/**
* 注解方式定义 事件监听器。 此时方法名可以自己定义
* @param event
*/
@EventListener
public void addMemberPoints(OrderPaySuccessEvent event){
//TODO 监听器处理 模拟新增会员积分逻辑
OrderBO orderBO = event.getOrderBO();
System.out.println(String.format("事件监听器处理,新增会员积分,orderId:%s",orderBO.getOrderId()));
}
}
复制代码
4、支持事件传播机制
Spring
支持事件的传播,比方说,当会员积分服务监听器给用户新增积分后,对满足条件的会员,需发送短信通知(比方说积分满 10w 分什么的),此时,就相当于 “订单支付成功事件” 触发了“会员新增积分”,然后“会员新增积分事件”又触发了"给用户发送短信",这就相当于一个事件逐级传递的过程。我们当然可以在会员积分监听器中,再添加一个事件发布器来发布事件,但是呢,Spring 提供了一种更简洁的方式来实现,我们只需对会员积分监听器中的方法进行简单调整即可,演示代码如下:
/**
* 注解方式定义 事件监听器。 此时方法名可以自己定义 (修改返回值为新增的事件对象,就可以实现 事件传递)
* @param event
* @return
*/
@EventListener
public AddMemberPointsEvent addMemberPoints(OrderPaySuccessEvent event){
//TODO 监听器处理 模拟新增会员积分逻辑
OrderBO orderBO = event.getOrderBO();
System.out.println(String.format("事件监听器处理,新增会员积分,orderId:%s",orderBO.getOrderId()));
return new AddMemberPointsEvent(this,event.getOrderBO());
}
复制代码
如演示代码所示,只需要修改监听器方法 addMemberPoints()
的返回值为新增的 AddMemberPointsEvent
事件对象,然后再添加一个监听器,去监听 AddMemberPointsEvent
事件,就可以实现事件的传递。
五、小结
1、观察者模式、事件模型都是非常优秀的设计思想,事件模型在一定程度上可以认为是观察者模式的一种变种/升级,没有好坏优劣之分,只是哪种方式更满足实际业务场景需要就用哪种就好了。
2、事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到事件对象后可以回调事件对象中的方法,这也是事件模型更灵活的地方。
3、JDK 为观察者模式、事件驱动模型都提供了抽象实现,这有利于我们在 Java 环境下通过这两种设计思想去实现我们的业务需求,但是,这只是代表 JDK 的一种实现方式。编程思想是相同的,只要理解了观察者模式、事件驱动机制的设计思想,我们完全可以自己自定义设计实现,其它的编程语言也一样可以实现。
4、Spring
框架在JDK
的基础上,提供了一整套更加完善、简洁的事件机制实现,进一步降低了使用事件机制实现业务场景的门槛,Spring
封装好了很多事件机制的通用步骤,用户可以不用关注底层框架实现,只需专注于自身具体的业务需求即可。
5、笔者为了方便对比演示观察者模式、事件模型的相似性,在事件模型的示例代码中,类、方法等命名都不够好,实际应用时,应当用更有含义的命名,比方说,监听器都加一个Listener
后缀,事件对象带 Event
关键字,监听器中的处理方法带 callback
、handler
、invoke
之类的关键字,这样可以增加代码的可读性。
越学越无知,我是老农小江,欢迎交流~
评论