深入理解事件发布监听机制

什么是事件发布监听

在软件程序开发过程中,经常会遇到“事件处理模型”:调用端发送一个事件,然后相应的事件处理程序处理这个事件。最常见的就数桌面应用软件了,在这些应用程序中,当你鼠标点击了一个按钮或者键盘输入一个快捷键时,应用程序就会做出响应,来处理你的点击和键盘输入等行为。

除了这些比较直观的桌面级应用程序外,事件驱动的设计模型还经常使用在程序设计中,它可以将事件发送和事件处理逻辑进行解耦,可以针对不同的事件定义对应的事件处理程序,增加了程序的扩展性,这种设计模式又被称为观察者模式。

在事件监听处理的过程中,主要有三个组件在工作:事件,事件发送组件和事件处理程序。在上面的举例中,鼠标点击,键盘输入等行为就是事件,发送事件组件的工作是向应用程序发送了“点击事件”和“键盘输入”事件,而事件处理程序就是定义的一系列的listener,来监听这些事件,并对事件做出处理。

很多时候,我们在使用事件驱动模型时,都是在事件处理框架的基础上使用的,在框架的基础上,我们只需要定义相应的事件处理方法就可以了,当对应的事件触发的时候,框架会自动调用事件处理方法,完成事件的处理。那事件处理框架是如何工作的呢?接下来我会使用一个完整的例子讲解一下事件处理的工作原理。

JDK中事件监听处理规范

其实在JDK中,已经定义了事件监听和处理的规范,用来对事件处理进行规范化。在JDK中,类EventObject是事件的抽象,主要包含事件来源,具体定义如下:

public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    protected transient Object  source;
 
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }
 
    public Object getSource() {
        return source;
    }
}

通常我们会基于该类进行业务事件扩展。

事件监听处理器 EventListener 是一个空接口

public interface EventListener {
}

在使用的时候,自定义的事件监听器继承或者实现该接口即可,虽然实现该接口,在功能实现上没有什么意义,但是在类型语义上,可以体现出该实现类是一个事件监听器,而且这个接口是许多事件处理框架的规范接口,继承或者实现该接口的自定义事件监听器,也会在其他事件处理框架更通用。

在JDK中没有定义事件发布组件,在使用的过程中,需要我们自己来定义。

上文说过,事件监听处理模式和观察者模式很像,其实事件监听机制就是在典型观察者模式基础上的进一步抽象和改进。这里我们做一下对照:
我们可以把主题(Subject)替换成事件(event)。

把对特定主题进行观察的观察者(Observer)替换成对特定事件进行监听的监听器(EventListener)。

而把原有主题中负责维护主题与观察者映射关系以及在自身状态改变时通知观察者的职责从中抽出,放入一个新的角色事件发布器(EventPublisher)中。

为了更方便理解,我把三者的依赖关系用类图表示一下:
在这里插入图片描述
为了更加详细的了解事件监听机制原理,下面我们使用JDK事件处理规范,实现一个简单的任务状态变化事件处理程序。

实现一个事件监听处理框架

一个事件监听处理框架主要有三个组件构成:事件定义,事件发布器,事件监听器。

事件定义

1.事件源:任务事件触发的源头–任务本身

/**
 * 任务
 */
public class Task {
    private String taskStatus;

    public Task(String taskStatus) {
        this.taskStatus = taskStatus;
    }

    @Override
    public String toString() {
        return "Task{" +
                "taskStatus='" + taskStatus + '\'' +
                '}';
    }
}

2.事件定义

/**
 * 任务状态改变事件
 */
public class TaskEvent extends EventObject {

    public TaskEvent(Task source) {
        super(source);
    }
}

具体描述Task任务状态变化的事件定义如下:
任务初始化事件

/**
 * 初始化事件
 */
public class TaskInitEvent extends TaskEvent {

    public TaskInitEvent(Task source) {
        super(source);
    }

    @Override
    public String toString() {
        return "TaskInitEvent{" +
                "source=" + source +
                '}';
    }
}

任务处理中事件

/**
 * 执行事件
 */
public class TaskProcessEvent extends TaskEvent {

    public TaskProcessEvent(Task source) {
        super(source);
    }

    @Override
    public String toString() {
        return "TaskProcessEvent{" +
                "source=" + source +
                '}';
    }
}

任务完成事件

/**
 * 完成事件
 */
public class TaskFinishEvent extends TaskEvent {

    public TaskFinishEvent(Task source) {
        super(source);
    }

    @Override
    public String toString() {
        return "TaskFinishEvent{" +
                "source=" + source +
                '}';
    }
}
事件监听器

事件监听器,就是定义对可以处理的事件进行处理逻辑的类

/**
 * task状态改变事件监听器
 */
public interface TaskEventListener<T extends TaskEvent> extends EventListener {

    /**
     * 事件处理
     */
    void onEvent(T eventObject);

    /**
     * 事件监听器可以处理的事件类型
     */
    default boolean isSupportEventType(TaskEvent taskEvent) {
        Type genericInterface = getClass().getGenericInterfaces()[0];
        Class<T> tClass = (Class<T>)((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        Class<? extends TaskEvent> taskEventClass = taskEvent.getClass();
        return tClass.isAssignableFrom(taskEventClass);
    }
}

接口定义了一个方法 onEvent,用来处理对应的事件,具体处理逻辑,在实现类中实现,同时在个接口中实现了一个默认方法:根据泛型类型,判断监听器可以可以处理的事件类型,这里表示的是:泛型指定的事件类类型或者泛型指定事件类的子类。

具体的事件监听器定义如下:
任务初始化事件监听器:

/**
 * 初始化事件处理器
 **/
public class TaskInitEventListener implements TaskEventListener<TaskInitEvent> {

    @Override
    public void onEvent(TaskInitEvent eventObject) {
        System.out.println("处理初始化事件: "+eventObject.toString());
    }
}

任务执行中事件处理器

/**
 * 执行中事件处理器
 **/
public class TaskProcessEventListener implements TaskEventListener<TaskProcessEvent> {

    @Override
    public void onEvent(TaskProcessEvent eventObject) {
        System.out.println("处理执行中事件: "+eventObject.toString());
    }
}

任务完成事件处理器:

/**
 * 已完成事件处理器
 **/
public class TaskFinishEventListener implements TaskEventListener<TaskFinishEvent> {

    @Override
    public void onEvent(TaskFinishEvent eventObject) {
        System.out.println("处理已完成事件: "+eventObject.toString());
    }
}
事件发布器

事件发布器,主要用来发布事件,同时事件发布器持有所有的事件监听器实例,这一点和观察者模式是完全一样。

/**
 * 事件发送器
 */
public class TaskEventPublisher {

    private List<TaskEventListener> listners=new ArrayList<>();

    /**
     * 注册监听器
     */
    public synchronized void register(TaskEventListener listner){
        if(!listners.contains(listner)){
            listners.add(listner);
        }
    }

    /**
     * 发布任务,并找到指定的listener进行处理
     */
    public void publishEvent(TaskEvent event){
        List<TaskEventListener> matchedListener = getMatchedListener(event);
        if(matchedListener.isEmpty()) {
            System.err.println("no matched listener exists");
            return ;
        }
        matchedListener.forEach(listener -> listener.onEvent(event));
    }

    /**
     * 获取符合条件的事件处理器
     */
    private List<TaskEventListener> getMatchedListener(TaskEvent event) {
        return listners.stream().
                filter(listener -> listener.isSupportEventType(event))
                .collect(Collectors.toList());
    }
}

在事件发布器中,事件监听器的注册,实际上就是将事件监听器添加到事件发布器持有的事件监听器集合中。事件发布就是从事件监听器集合中,找出可以处理被发布事件的事件监听器,对发布的事件进行处理。

应用程序

上面已经完成了对 事件定义,事件监听器,事件发布器的定义,接下来就是如何将三者集成在一起,是整个事件监听处理框架工作起来。

/**
 * 应用程序
 */
public class EventApplication {

    public static void main(String[] args) {

        TaskEventPublisher taskEventPublisher = getTaskEventPublisher();
        for (TaskEventListener taskEventListener : getAllListener()) {
            taskEventPublisher.register(taskEventListener);
        }
        Task initTask = new Task("init");
        TaskInitEvent initEvent = new TaskInitEvent(initTask);
        taskEventPublisher.publishEvent(initEvent);
    }

    /**
     * 获取事件监听器
     */
    private static List<TaskEventListener> getAllListener(){
        List<TaskEventListener> allListeners = new ArrayList<>();
        allListeners.add(new TaskInitEventListener());
        allListeners.add(new TaskProcessEventListener());
        allListeners.add(new TaskFinishEventListener());
        return allListeners;
    }

    /**
     * 获取事件发布器
     */
    private static TaskEventPublisher getTaskEventPublisher(){
        return new TaskEventPublisher();
    }
}

使用 EventApplication 可以将整个事件监听处理的各个组件集成在一起工作。到这里整个事件监听处理框架就完成了,事件处理的原理你大概也明白了吧。

其实在spring中的事件处理流程大概也是类似的,也分为事件发布器,事件定义和事件监听器,只不过在spring中有IOC容器帮助,我们只需要定义事件和事件监听器即可,spring会自动事件监听器注册到事件发布器中,我们只需要在业务逻辑中发布相应的事件即可,除此之外,spring在事件处理流程中,还实现了一些特性。比如,spring还会对事件监听器和事件处理类型进行缓存,提升根据事件类型找到事件处理器的效率,同时spring还会定义事件处理执行器,实现事件处理的异步化等。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值