精华内容
下载资源
问答
  • 事件驱动模型

    千次阅读 2020-05-22 00:00:52
    事件驱动模型的概念、作用、实现思路以及几种不同的实现方式。

    1 概念

    2 作用

    3 实现思路

    3.1 三要素

    3.2 三要素之间的关系

    3.3 常见的实现形式

    3.4 同步驱动和异步驱动

    4 经典实现

    5 示例


    1 概念

    在一些应用场景中,我们希望程序是被“事件”触发运行的,并且程序在感知到不同的“事件”后能够产生不同的响应动作(例如常见的UI应用程序,在用户点击不同的按钮后会产生不同的动作效果),此时就需要应用程序能够实时“感知”其所关心的事件,并在事件发生后执行相应的操作。

    在解决上述问题时,应用程序是由“事件”驱动运行的,这类程序在编写时往往可以采用相同的模型实现,我们可以将这种编程模型称为事件驱动模型

    个人理解:事件驱动模型其实是一种抽象模型,用于对由外部事件驱动系统业务逻辑这类应用程序进行建模。

    2 作用

    基于事件驱动的应用程序可以实时响应所关心的事件,实现实时检测、响应外部动作,这是事件驱动模型的基本功能和作用。在一些复杂的系统中,事件驱动模型还可很好地发挥以下作用:实现组件之间的松耦合/解耦实现异步任务跟踪状态变化

    • 实现组件之间的解耦

    在复杂系统中,往往存在多个组件相互耦合的情况,如果将组件之间的耦合关系抽象成“事件(event)”,让事件担任组件之间的通信任务,就能而降低\解除组件之间的耦合关系。

    参考以下示例

    //示例1    经典实现,类A、B之间的耦合关系较强耦合
    class A {
        void act1() {}
        void act2() {}
        //...
    }
    
    class B {
        A a;
        void action() {
            //TODO 根据业务逻辑判断需要触发a的哪个动作
        }
        //....
    }

    示例1的代码段中,类B对类A有较强的依赖,B需要根据不同的业务逻辑调用A的不同动作,若类A的实现有变化那么类B难免需要进行适配。如果将A、B之间的耦合关系抽象为事件,代码见示例2

    //示例2    将A、B的耦合关系抽象为事件,以实现解耦
    class Event {
        //...
    }
    
    class A {
        void notify(Event e) {
            //TODO 判断事件类型,触发响应动作
        }
        //...
    }
    
    class B {
        A a;
        void action() {
            Event e = craeteEvent();
            a.notify(e);
        }
    
        Event createEvent() {
            Event e = new Event();
            //...
            return e;
        }
        //...
    }

    此时B将更多的关注点转移到了Event,与A的关系仅限于B通知A需要处理业务,具体触发什么动作由A自己判断并触发。相比于示例1的实现,示例2的实现中引入了类Event辅助类A、B之间通信,类A与类B之间的耦合关系也更松弛。

    个人理解:事件驱动模型,实际上是将组件之间的耦合关系转移到了“事件(Event)”上,但是对于某个领域而言事件(Event)一般具有通用性并且不会频繁变更实现逻辑,所以事件驱动模型可以很好地实现组件之间的解耦。

    • 实现异步任务

    在一些业务场景中,顺序、阻塞式地执行任务会遇到一些比较耗时的中间步骤,但是不希望整个流程都停下来等待这些中间步骤完成,而是触发一个异步操作然后继续执行当前任务,在收到异步操作处理完成的消息之后再执行相关的处理。

    打一个比较容易理解的比方:上课的时候需要记学习笔记,但是笔记内容较多的时候,老师不会停下来等大家做完笔记再继续讲课,大家只能先记住“记笔记”这件事,等老师讲完课后在把笔记补上,有时候我们补完笔记还需要告诉老师“笔记已经做好了”,请老师检查笔记。

    当然,也可以是多线程下的异步任务:如果有的同学具备一心二用的能力,他就可以在听课的同时做完笔记,然后告诉老师他已经完成了“记笔记”的任务。

    在一些事件驱动模型的设计结构中,会存在“任务队列”用以存储需要处理的“事件(Event)”,这种结构的事件驱动模型就可以很好地实现上述业务场景。

    使用事件驱动模型实现异步任务的一般思路是:当遇到耗时较大、没有同步执行要求的操作时,针对这个操作触发一个事件,将这个事件加入到任务队列中,直到有一个进程(线程)能够获取并执行这个任务,才开始执行这个任务。

    • 跟踪状态变化

    在存储实体模型的业务中通常需要修改实体模型的数据,对于部分业务场景需要存储、使用实体模型的历史变更记录,例如什么时间对实体数据做了什么修改。对于这类需求,事件驱动模型也可以提供很好的解决方案,我们可以认为每次修改实体数据都是一次事件,那么在修改实体数据后将这个事件存储到事件队列中即可实现跟踪状态变化的需求。

    待补充...

    对于事件驱动模型作用的的理解,可以参考:事件驱动架构设计

    3 实现思路

    事件驱动模型有很多种体现形式,如简单的事件触发机制、单线程异步任务、多线程异步任务等,但是各种技术中实现事件驱动模型的基本思路相同。

    事件驱动模型的基本结构如图1所示

    图 1 事件驱动模型基本结构

    事件驱动模型包括三个基本要素:事件状态对象(Event)事件源(EventSource)事件监听器(EventListener)

    3.1 三要素

    • 事件状态对象(Event)

    Event作为事件的抽象,对事件本身进行了描述,携带着事件的详细信息(如在UI系统中,Event携带的信息有鼠标动作类型、鼠标坐标等信息)。在事件驱动模式中Event起着传递事件信息的作用,负责把被触发事件的详细信息传递给EventListener,以便EventListener作出正确的响应动作。

    • 事件源(EventSource)

    EventSource主要用于感知外部事件,将事件打包成Event并分发给EventListener处理。

    个人理解:这里的EventSource实际上担任了两个职责:1)生成事件;2)派发事件。依据职责单一原则的描述EventSource应该只担任“生成事件”的职责,而“派发事件”的职责应当重新设计一个EventDispatcher类来担任,但是由于单应用场景下“派发事件”的业务逻辑通常比较简单,所以这里将EventDispatcher的职责也交由EventSource负责。

    PS:到底应该设计成EventSource还是 EventSource EventDispatcher ,涉及到事件驱动模型的两种实现形式:以观察者模式实现、以发布订阅模式实现,3.3节会具体谈到。

    • 监听器(EventListener)

    EventListener封装了响应事件的能力,它职责就是在感知到EventSource触发Event后,处理Event对象以响应事件。一般需要在EventSource预留注册EventListener的接口,以便在运行时向EventSource添加处理事件的监听器。

    3.2 三要素之间的关系

    关系对关系
    EventSource -- EventListenerEventSource注册EventListener
    EventSource通知EventListener处理Event
    Event -- EventSourceEventSource生成Event,并派发Event
    Event -- EventListenerEventListener消费Event

    3.3 常见的实现形式

    事件驱动模型有两种实现形式:以观察者模式实现以发布/订阅模式实现。下面先介绍一下关于观察者模式发布/订阅模式的区别。详细内容可以参考发布订阅模式与观察者模式

    观察者模式订阅/发布模式的主要区别在于两者触发事件的机制不同,如图2所示(借同业绘制的对比图一用,侵删)

    图片描述
    图 2 观察者模式与发布/订阅模式对比图

    上图是为了对比观察者模式订阅/发布模式两者之间的区别,所以图中各模块的名字遵循了规范的写法。本文的目的在于对比两个模式在实现事件驱动模型时的区别,为了方便后文描述,将上图各的名字作如下映射:

    • EventSource:表示SubjectPublisher

    SubjectPublisher在模型中担任的主要职责都是生成EventSubject还担任了简单的分发Event的职责)。

    • EventDispatcher:表示Topic/EventChannel

    图中的“Topic/EventChannel”3.1节讨论的EventDispatcher(调度器)担任的职责相同(调度Event),本人更倾向于使用“调度器”来描述这个模块l

    • EventListener:表示ObserverSubscriber

    ObserverSubscriber在模型中担任的职责都是监听、处理Event

    图2可见,订阅/发布模式直观上来看比观察者模式多了一个EventDispatcher模块,并且EventListener的依赖关系也从EventSource转移到了EventDispatcher上。观察者模式订阅/发布模式存在这种直观上的差异,是因为两者解决问题的场景不同:

    • 观察者模式关注的是事件发生后观察者能够获取到足够的数据。这种场景下Event种类单一,但是Event会被多个EventListener关注,Event中携带的信息更多的是观察者需要的数据。这种场景下,要求事件发生后EventSource能够通知所有EventListener,此时调度Event的逻辑很简单,不必设置独立的调度器执行调度任务。

    PS:如果需要实现异步任务,EventSource中会设计一个事件队列用于异步调度

    • 订阅/发布模式关注的是事件发生后能够准确地触发相应的动作。这种场景下Event种类多,每类Event只有多个EventListener关注,Event中携带的多是事件自身的一些信息(例如EventID之类),而很少携带其他数据。这种场景下,Event更像一个触发信号,不同的Event会触发不同的EventListener执行动作,此时调度Event的逻辑复杂,需要设置独立的调度器执行调度任务。

    PS:如果需要实现异步任务,EventDispatcher中会设计一个事件队列用于异步调度

    观察者模式订阅/发布模式的具体差异对比见下表

     观察者模式发布/订阅模式

    EventSource与EventListener

    的耦合关系

    松耦合完全解耦
    角色(除Event外)

    2个

    EventSource、EventListener

    3个

    EventSource、EventDispatcher、EventListener

       
    EventSource个数一个可能存在多个
    事件种类数一种多种
    EventListener种类数多种多种
    消息中间件不存在存在:EventDispatcher
    事件与监听器对应关系一个事件对应多个监听器一个事件对应多个监听器
    Event携带的内容数据一般不携带除描述Event自身信息以外的数据
    应用场景独立应用程序多组件的应用程序、夸应用的系统

    在具体的业务中是选择观察者模式还是发布/订阅模式来实现事件驱动模型,取决于具体的业务场景和对Event的抽象粒度。如果系统中只有一种Event的抽象,优先考虑使用观察者模式实现;如果系统中存在多种Event抽象,优先考虑发布/订阅模式实现。

    对于Event该如何抽象也因人而异。例如在《HeadFirst Pattern》一书讲解观察者模式的那个例子,气象站检测到的数据有温度、气压、湿度等,如何对事件进行抽象就有两种思维:

    1)认为事件就是气象数据变化,无论温度、气压还是湿度的一项或者多项有变化,都触发同一个事件。

    2)认为温度变化、气压变化、湿度变化是三种不同的事件,当其中的一项数据变化时触发相应的事件,如果三项数据同时变化将会触发三个事件。

    对于1)中的抽象结果,可能使用观察者模式更合适;而对于2)中的抽象结果,则使用发布/订阅模式更合适。可见对于相同的应用场景,不同的抽象方式会有不同的使用情景。

    PS:经过对观察者模式发布/订阅模式的对比,容易发现本文之前举的例子(示例2图2)其实都是以观察者模式实现的。

    个人理解观察者模式发布/订阅模式都可以实现事件驱动模型。在实现事件驱动模型时可以选用观察者模式发布/订阅模式中更适合当前业务场景的模式组织编码,具体如何实现可以参考观察者模式发布/订阅模式的资料,也可以参考后文给出的案例链接。

    3.4 同步驱动和异步驱动

    在第2节中我们已经了解了异步任务的含义,也对异步任务的实现思路做了简单介绍。相比于同步任务异步任务的优势在于处理耗时长的任务时不会阻塞主线任务。

    大家在比较同步任务异步任务时,一般还会和多线程任务一起进行对比,本文不对这三种时序模式作太详细的介绍,这里借用一张比较经典的对比图(如图3)来解释一下三者的区别,如大家想了解更多可以查阅同步、异步、多线程等相关知识。

    图 3 同步、异步、多线程对比
    • 单线程同步模型:三个任务依次顺序执行,并且只有当某个任务完全执行完成后才能执行下一项任务,如果这三个任务之间不存在依赖关系,但是任务中存在阻塞操作(如IO操作),程序将会存在大量的等待时间,导致程序的运行速度变慢。
    • 多线程模型:三个任务运行在不同的线程中,任务之间不会彼此影响。这种模型往往需要考虑多线程同步的问题,处理起来难度相对较大,并且对于每个任务而言线程仍然会存在等待时间,只是缩短了整个程序的等待时间、提高了CUP利用率。实现方式参考多线程相关知识。
    • 异步模型:三个任务仍然在同一个线程中执行,只是在当前任务遇到阻塞操作时会让出资源,让其他任务先执行,如此交替执行。这种模式在遇到阻塞操作时不会等待,所以程序运行速度会快很多。异步模型的实现思路可以参考第2节的描述,需要维护一个任务队列,当遇到阻塞操作时将任务放到队列中,阻塞操作完成后将任务从队列中取出等待执行机会,在获得执行机会后执行回调函数继续处理任务。

    PS:这三种模型没有好坏之分,在开发时选用哪种模型取决于具体的业务场景。)

    经过上述比较,我们可以清晰地认识到同步模型异步模型在实现上的区别,异步模型需要借助任务/事件队列实现。

    在事件驱动模型中,驱动事件的动作可以选择同步驱动异步驱动

    同步驱动事件:生成事件、触发事件和处理事件是顺序执行的。EventSource在生成Event后立即通知EventListener,事件处理完成后返回处理结果。具体实现参考观察者模式的经典实现。

    异步驱动事件:生成事件、触发事件和处理事件不是顺序执行的。EventSource在生成Event后将Event缓存在事件队列中,然后通知EventListener,事件处理完成后如果有必要再调用回调函数上报处理结果。有时我们会采用多线程的方式,由线程A向队列缓存Event由线程B消费Event

    基于异步驱动实现事件驱动模型时,还需要参考消息队列和生产者-消费者模式相关知识来维护事件队列。

    4 经典实现

    观察者模式

    订阅/发布模式

    UI框架

    Netty

    Spring 事件模型

    Node.js

    5 示例(点击链接查看)

    展开全文
  • 基于epoll的echo server,用回调函数封装了epoll,以后会添加序列化和粘包处理,以及心跳,慢慢完善。
  • 在了解Node.js事件驱动模型之前,我们先了解一下传统的线程网络模型,请求进入web服务器(IIS、Apache)之后,会在线程池中分配一个线程来线性同步完成请求处理,直到请求处理完成并发出响应,结束之后线程池回收。...
  • Redis这个数据库相信不用过多介绍了,大家应该都知道,下面这篇文章主要给大家介绍了关于Redis中事件驱动模型的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
  • 深入分析Spring事件驱动模型

    万次阅读 多人点赞 2017-11-18 11:12:59
    聊一聊从观察者模式到Spring的事件驱动模型

    一、事件驱动模型简介

    事件驱动模型,也即是我们通常说的观察者。基于发布-订阅模式的编程模型。

    概念
    定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并自动更新。

    百度百科:
    从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。
    事件收集器专门负责收集所有事件,包括来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件的(如操作系统、应用程序本身等)。
    事件发送器负责将收集器收集到的事件分发到目标对象中。
    事件处理器做具体的事件响应工作。

    从程序设计的角度来看,事件驱动模型的核心构件通常包含以下几个:

    1. 事件源:负责产生事件的对象。比如我们常见的按钮,按钮就是一个事件源,能够产生“点击”这个事件

    2. 事件监听器(事件处理器):负责处理事件的对象

    3. 事件:或者称为事件对象,是事件源和事件监听器之间的信息桥梁。是整个事件模型驱动的核心

    下图展示了事件、事件源、监听器直接的关系:
    事件驱动模型

    举个例子:

    产品经理经过百般调研和思考过后提出开发一个新系统,确定需求后将需求录入到需求管理系统,这时开发人员得知确认需求后开始进入技术开发阶段。

    在这个例子中,“产品经理”就是一个事件源,事件对象是“开发一个新系统”,然后“开发人员”就是一个事件监听器或者事件处理器,负责新系统的开发。那么此处为什么是开发人员开发需求,而不是销售人员开发需求呢?所以一个事件监听器要能够处理某个事件,需要提前建立某种关系。这种关系的建立采用事件驱动模型来表达就是注册或者订阅。当某种关系建立后,事件监听器才能处理相关联的事件,也就是开发人员来开发需求,而不是销售人员来开发需求。

    事件监听器监听其感兴趣的事件,当事件源发布事件时,便能得到通知。

    二、事件模型的应用

    基于事件模型驱动的程序设计,笔者在开发过程中比较常遇到的关于事件驱动模型的应用包括以下几类:

    1. 观察这模式
    2. JDK观察者模式
    3. JavaBean事件驱动
    4. Spring事件驱动

    下面对着4类事件驱动模型进行详细介绍,包括原理介绍和给出可运行的案例。

    1、 观察者模式

    原理解析

    观察者模式的UML图如下:

    观察者模式UML

    具体类说明如下:

    1. Observer观察者,即为事件模型中的事件监听器,该接口定义一个方法update,即为事件处理方法。当观察到有事件产生时,该方法便会处理
    2. Subject被观察者,即事件模型中的事件源,负责产生事件,定义与观察者建立关联的方法(添加观察者、删除观察者、通知观察者)
    3. ConreteObserver具体的观察者实现类,实现Observer接口中的update方法,具体实例会被添加到被观察者的观察者队列中(observers[List])
    4. ConreteSubject具体的被观察者实现类,实现Subject接口。定义观察者队列(observers[List]),并定义实现如何将观察者对象添加到观察者队列中以及如何通所有知观察者

    在上述类图中,具体的ConreteSubject被观察者,其中包含observers一个列表,保存所有观察者对象。doAction方法是需要通知观察者对象的动作,当该方法执行后,会通知保存在observers中的所有观察者。

    也就是在执行方法ConreteSubject#doAction()时,需要调用ConreteSubject#notifyObservers()通知保存在observers中的所有观察者,让其能够做出相应。

    案例

    上面描述感觉有点晦涩,我们举个例子:

    以支付状态更新通知为例,自定义观察者设计模式,当支付状态更新时,通知邮件服务和库存服务。

    1、 Observer接口及实现类

    /**
     * Created by zhurudong on 2017/10/21.
     * 观察者
     */
    public interface Observer {
        void update();
    }
    
    /**
     * Created by zhurudong on 2017/10/21.
     * 库存服务
     */
    public class MyStockObserver implements Observer {
        @Override
        public void update() {
            System.out.println("库存服务收到通知");
        }
    }
    
    
    /**
     * Created by zhurudong on 2017/10/21.
     * 邮件服务
     */
    public class MyEmailObserver implements Observer{
        @Override
        public void update() {
            System.out.println("邮件服务收到通知.");
        }
    }

    2、 Subjest接口及实现类

    /**
     * Created by zhurudong on 2017/10/21.
     * 被观察者
     */
    public interface Subject {
        /**
         * 添加观察者
         * @param observer
         */
        void addObserver(Observer observer);
    
        /**
         * 删除观察者
         * @param observer
         */
        void removeObserver(Observer observer);
    
        /**
         * 通知观察者
         */
        void notifyObservers();
    }
    
    /**
     * Created by zhurudong on 2017/10/21.
     *
     * 更改支付状态的被观察则
     */
    public class MyPaymentStatusUpdateSubject implements Subject {
    
        private List<Observer> observers = new Vector<>();
    
        @Override
        public void addObserver(Observer observer) {
            observers.add(observer);
        }
    
        @Override
        public void removeObserver(Observer observer) {
            observers.remove(observer);
        }
    
        @Override
        public void notifyObservers() {
            for (Observer observer : observers) {
                observer.update();
            }
        }
    
        public void updatePaymentStatus(int newStatus) {
    
            // 业务逻辑操作
            System.out.println("更新新的支付状态为:" + newStatus);
    
            // 通知观察者
            this.notifyObservers();
        }
    }

    3、 测试类

    /**
     * Created by zhurudong on 2017/10/21.
     *
     * 观察者模式
     */
    public class Test {
        public static void main(String[] args) {
    
            // "支付状态更新"->看做一个事件,可以被监听的事件
    
            // 被观察者。即事件源
            MyPaymentStatusUpdateSubject myPaymentStatusUpdateSubject = new MyPaymentStatusUpdateSubject();
    
            // 观察者。即事件监听器
            MyEmailObserver myEmailObserver = new MyEmailObserver();
            MyStockObserver myStockObserver = new MyStockObserver();
    
            // 添加观察者。
            myPaymentStatusUpdateSubject.addObserver(myEmailObserver);
            myPaymentStatusUpdateSubject.addObserver(myStockObserver);
    
            // 发布事件。支付状态更新。
            myPaymentStatusUpdateSubject.updatePaymentStatus(2);
        }
    }

    在这个例子中邮件服务和库存服务就是监听器,监听“支付状态变更”这一事件,由事件源MyPaymentStatusUpdateSubject产生此事件,当调用myPaymentStatusUpdateSubject#updatePaymentStatus(2);方法发布事件时,会通知所有的监听器,即库存服务和邮件服务,他们根据自己的业务逻辑做出不同的响应。

    2、JDK中的观察者模式

    原理解析

    JDK已经帮我们实现了一个观察者模式,事件收集器采用Vector容器保存,操作事件收集器的方法都是线程安全的。

    UML图如下:
    JDK观察者模式

    跟上面我们自己实现的观察者模式十分类似,具体类说明如下:

    1. Observer观察者
    2. Observable可观察,即被观察者,是一个具体的类
    3. ConreteObserver具体的观察者实现类
    4. ConreteObservable具体的被观察者实现类,继承Observable类

    观察发现,Observable可观察,即被观察者,已经实现了添加观察者、删除观察者、通知观察者等等操作事件收集器的方法,不用每个子类都要去实现一遍,只需要按照它的方式使用接口。

    不同的是,我们做的是在执行方法ConreteObservable#doAction()时,在调用Observable#notifyObservers()通知观察者之前需要调用Observable#setChanged()表示可观察者状态已经发生改变,观察者可以对此做出想用。

    案例

    我们举个栗子:
    还是“以支付状态更新通知为例,自定义观察者设计模式,当支付状态更新时,通知邮件服务和库存服务。”,下面看看类之间的关系:

    1、 观察者接口

    摘选JDK源码

    public interface Observer {
        /**
         * This method is called whenever the observed object is changed. An
         * application calls an <tt>Observable</tt> object's
         * <code>notifyObservers</code> method to have all the object's
         * observers notified of the change.
         *
         * @param   o     the observable object.
         * @param   arg   an argument passed to the <code>notifyObservers</code>
         *                 method.
         */
        void update(Observable o, Object arg);
    }

    2、可观察对象

    摘选自JDK源码

    package java.util;
    
    /**
     * @author  Chris Warth
     * @see     java.util.Observable#notifyObservers()
     * @see     java.util.Observable#notifyObservers(java.lang.Object)
     * @see     java.util.Observer
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     * @since   JDK1.0
     */
    public class Observable {
        private boolean changed = false;
        private Vector<Observer> obs;
        /** Construct an Observable with zero Observers. */
        public Observable() {
            obs = new Vector<>();
        }
    
        public synchronized void addObserver(Observer o) {
            if (o == null)
                throw new NullPointerException();
            if (!obs.contains(o)) {
                obs.addElement(o);
            }
        }
    
        public synchronized void deleteObserver(Observer o) {
            obs.removeElement(o);
        }
    
        public void notifyObservers() {
            notifyObservers(null);
        }
    
        public void notifyObservers(Object arg) {
            Object[] arrLocal;
            synchronized (this) {
                /* 这里存在2个潜在的糟糕结果:
                 * 1) 一个新加入的观察者错过了正在执行的通知
                 * 2) 一个刚刚删除的观察者不需要关心变更时有可能会错误地被通知
                 */
                if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(this, arg);
        }
    
        public synchronized void deleteObservers() {
            obs.removeAllElements();
        }
    
        protected synchronized void setChanged() {
            changed = true;
        }
    
        protected synchronized void clearChanged() {
            changed = false;
        }
    
        public synchronized boolean hasChanged() {
            return changed;
        }
    
        public synchronized int countObservers() {
            return obs.size();
        }
    }

    3、邮件服务和库存服务

    /**
     * Created by zhurudong on 2017/10/21.
     * 邮件服务
     */
    public class EmailObserver implements Observer {
        @Override
        public void update(Observable o, Object arg) {
            System.out.println("邮件服务收到通知");
        }
    }
    
    /**
     * Created by zhurudong on 2017/10/21.
     * 库存服务
     */
    public class StockObserver implements Observer {
        @Override
        public void update(Observable o, Object arg) {
            System.out.println("库存服务收到通知");
        }
    }

    4、支付服务类

    /**
     * Created by zhurudong on 2017/10/21.
     *
     * 支付状态变更做为一个可观察者
     */
    public class PaymentStatusObservable extends Observable {
    
        public void updatePaymentStatus(int newStatus) {
            // 业务逻辑操作
            System.out.println("更新新的支付状态为:" + newStatus);
    
            // 通知观察者
            this.setChanged();//需要调用一下这这方法,表示被观察者的状态已发生变更,Observable才会通知观察者
            this.notifyObservers();
        }
    }

    5、测试类

    /**
     * Created by zhurudong on 2017/10/21.
     *
     * 使用JDK中的观察者模式
     */
    public class Test {
    
        public static void main(String[] args) {
    
            // "支付状态更新"->看做一个事件,可以被监听的事件
    
            // 被观察者。即事件源
            PaymentStatusObservable paymentStatusObservable = new PaymentStatusObservable();
    
            // 添加观察者
            paymentStatusObservable.addObserver(new EmailObserver());
            paymentStatusObservable.addObserver(new StockObserver());
    
            // 支付状态变更
            paymentStatusObservable.updatePaymentStatus(3);
        }
    }

    对于简单的事件模型驱动设计的程序采用JDK提供的API已经够用了,Observable类把观察者模式用到的方法几乎都实现了,省去了许多操作事件的编码工作。

    但也正因为如此,Observable把部分的编码操作实现了,导致灵活性降低了,没有很好地进行抽象,而是几乎是给出了所有的实现。因此很多框架例如Spring在实现事件驱动的时候,没有直接使用JDK提供的API,而是大部分定义了自己的事件驱动模型。

    我们针对JDK给出的观察者模式的解决方案总结几点:

    1. Observable实现了大部分的逻辑,没有很好地进行抽象,灵活性降低了

    2. 存在2个潜在的问题:一个刚刚加入的观察者错过了通知;一个刚刚删除的观察者被错误的通知

    3. Observable实现的方法采用synchronized,操作同一个方法时串行,可能会存在效率问题

    3、JavaBean中的事件驱动

    原理解析

    javabean规范中提供了一种监听属性变化的事件驱动模型。提供操作Javabean属性的类PropertyChangeSupport和PropertyEditorSupport。

    从事件驱动模型的各个构建来分析,PropertyChangeSupport和PropertyEditorSupport都属于事件源,能够产生事件。如果以观察模式的角度来看,则是被观察者

    事件监听器实现PropertyChangeListener接口,通过PropertyChangeSupport和PropertyEditorSupport添加到事件队列中。

    一旦有Javabean属性发生变化通过调用PropertyChangeSupport#firePropertyChange()、PropertyEditorSupport#firePropertyChange()即可触发监听器处理事件。

    JDK提供的API给我们展示了PropertyChangeSupport的用法,照葫芦画瓢写一个自己的示例。

    案例

    当一条订单支付状态更新时能够实时收到变更的通知。

    1、事件源

    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeSupport;
    /**
     * Created by zhurudong on 2017/10/21.
     */
    public class PaymentStatusUpdateBean {
    
        PropertyChangeSupport paymentlisteners = new PropertyChangeSupport(this);
    
        public void updateStatus(int newStatus) {
            // 模拟业务逻辑
            System.out.println("支付状态更新: " + newStatus);
            // 触发通知
            paymentlisteners.firePropertyChange("paymentStatuUpdate", 0, newStatus);        ;
        }
    
        public void addPropertyChangeListener(PropertyChangeListener listener) {
            paymentlisteners.addPropertyChangeListener(listener);
        }
    
        public void removePropertyChangeListener(PropertyChangeListener listener){
            paymentlisteners.addPropertyChangeListener(listener);
        }
    }

    2、事件监听器

    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    
    /**
     * Created by zhurudong on 2017/10/21.
     * 支付状态变更的监听器
     */
    public class PaymentStatusUpdateListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            System.out.printf("支付状态变更. eventName : %s, oldValue : %s, newValue : %s", evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
        }
    }

    3、测试类

    /**
     * Created by zhurudong on 2017/10/21.
     * 测试JDK提供的PropertyChangeEvent
     */
    public class Test {
        public static void main(String[] args) {
            PaymentStatusUpdateBean paymentStatusUpdateBean = new PaymentStatusUpdateBean();
    
            // 添加监听器
            paymentStatusUpdateBean.addPropertyChangeListener(new PaymentStatusUpdateListener());
    
            // 更新支付状态
            paymentStatusUpdateBean.updateStatus(3);
        }
    }

    通过设定的PaymentstatusUpdateListener监听器可以实时地感知到支付状态发生的变化。

    4、Spring事件驱动

    Spring事件驱动模型原理比较复杂,涉及到的类比较多,我们先来看一个案例,从一个案例入手再逐渐理解Spring事件驱动模型。

    1、 案例

    跟之前的例子一样,当一个订单的支付状态发生变化时,能够通知到邮件服务、短信服务、库存服务。

    首先先定义一个支付实体类,作为事件传输的信息载体

    /**
     * Created by zhurudong on 2017/10/22.
     * 支付实体类
     */
    public class PaymentInfo {
    
        private int id;
        private String stauts;
    
        public PaymentInfo(int id, String stauts) {
            this.id = id;
            this.stauts = stauts;
        }
        // 省略setter getter ....
        @Override
        public String toString() {
            return "PaymentInfo{" +
                    "id=" + id +
                    ", stauts='" + stauts + '\'' +
                    '}';
        }
    }

    然后定义一个支付状态更新的事件,事件以PaymentInfo作为传输的载体,构建事件时需要将PaymentInfo实例化并传给PaymentStatusUpdateEvent事件对象

    /**
     * 支付状态更新事件
     */
    public class PaymentStatusUpdateEvent extends ApplicationEvent {
        /**
         * Create a new ApplicationEvent.
         *
         * @param source the object on which the event initially occurred (never {@code null})
         */
        public PaymentStatusUpdateEvent(PaymentInfo source) {
            super(source);
        }
    }

    定义事件监听器。为了满足各类场景的需求,伟大的Spring提供多种定义事件监听器。

    1. 有序事件监听器:当多个监听器共同监听同一个事件时,该类是监听器可以按照事先定义好的顺序先后执行监听器,达到不同的监听器有序执行的目的。

    2. 无序事件监听器:多个事件监听器听共同监听同一个监听器时,事件被触发后无法保证这些事件监听器的执行顺序。

    定义一个无序的事件监听器:库存服务监听器

    /**
     * Created by zhurudong on 2017/10/22.
     */
    @Component
    public class StockPaymentStatusUpdateListener implements ApplicationListener<PaymentStatusUpdateEvent> {
    
        @Override
        public void onApplicationEvent(PaymentStatusUpdateEvent event) {
            System.out.println("库存服务, 收到支付状态更新的事件. " + event + " - Thread: " + Thread.currentThread().getName());
        }
    }

    定义一个有序监听器:邮件服务监听器、库存服务监听器

    /**
     * Created by zhurudong on 2017/10/22.
     */
    @Component
    public class MailPaymentStatusUpdateListener implements SmartApplicationListener {
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
            return eventType == PaymentStatusUpdateEvent.class;
        }
    
        @Override
        public boolean supportsSourceType(Class<?> sourceType) {
            return sourceType == PaymentInfo.class;
        }
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println("邮件服务, 收到支付状态更新的通知. " + event+ " - Thread: " + Thread.currentThread().getName());
        }
    
        // 排序
        @Override
        public int getOrder() {
            return 1;
        }
    }
    
    @Component
    public class SmsPaymentStatusUpdateListener implements SmartApplicationListener {
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
            return eventType == PaymentStatusUpdateEvent.class;
        }
    
        @Override
        public boolean supportsSourceType(Class<?> sourceType) {
            return sourceType == PaymentInfo.class;
        }
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println("短信服务, 收到支付状态更新的通知. " + event+ " - Thread: " + Thread.currentThread().getName());
        }
    
        // 排序
        @Override
        public int getOrder() {
            return 2;
        }
    }

    上述3个类分别定义了一个无序监听器StockPaymentStatusUpdateListener和两个有序监听器MailPaymentStatusUpdateListener和SmsPaymentStatusUpdateListener。在有序监听器中可以看到xxx#getOrder()获取他们的执行顺序,数字越小执行的优先级越高。也就是当事件被触发后MailPaymentStatusUpdateListener会优于SmsPaymentStatusUpdateListener被执行。

    Spring的配置文件如下:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:task="http://www.springframework.org/schema/task"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
           " default-lazy-init="true">
    
        <context:component-scan base-package="com.event"/>
    
    </beans>

    测试类:

    /**
     * Created by zhurudong on 2017/10/22.
     * Spring事件驱动模型
     */
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    
            PaymentService paymentService = applicationContext.getBean(PaymentService.class);
    
            paymentService.pay(1, "支付成功");
        }
    
    }

    执行结果如下:

    通过上图我们知道,有序邮件服务和短信服务是有序的事件监听器,并定义了邮件服务优于短信服务先执行。所以程序多次运行后结果都是邮件服务先执行,然后是短信服务。

    仔细观察上图展示的结果我们发现,所有的监听器都是在主线程中执行的。这就可能会引发性能问题,我们能否将这些事件监听器放到额外的线程去执行,而不在主线程中执行呢?答案是有的。

    很方便Spring为我们提供了2种方式将事件执行放到另外的线程中执行

    第一种方式:全局异步
    先看看Spring文件是如何配置的

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:task="http://www.springframework.org/schema/task"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
           " default-lazy-init="true">
        <context:component-scan base-package="com.event"/>
    
        <!-- 任务执行器 -->
        <task:executor id="payment-status-update-thread" pool-size="10"/>
    
        <bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
            <property name="taskExecutor" ref="payment-status-update-thread"/>
        </bean>
    </beans>

    定义任务执行器,需要配置SimpleApplicationEventMulticaster#taskExecutor属性,将任务执行器配置

    <task:executor id="payment-status-update-thread" pool-size="10"/>

    注入到SimpleApplicationEventMulticaster中即可,监听器不需要做任何变动。

    需要注意的是实例化SimpleApplicationEventMulticaster时的名称必须为applicationEventMulticaster,是固定的,因为在AbstractApplicationContext类中查找事件触发器是通过定义好的bean的名称查找。

    关于SimpleApplicationEventMulticaster更多内容后文会有分析。

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    我们来看看执行结果:

    使用这种方式开启异步化执行事件,也就是所有的事件监听器执行事件时都会在配置的线程中执行。
    Spring还提供了一种比较灵活的方式配置,可以将部分的监听器设置为异步执行

    第二种方式:注解式配置异步

    采用注解的方式配置,将注解@Async放到ApplicationListener#onApplicationEvent(E event)监听器的实现类上,即可将监听器设置为异步化处理

    看看spring配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:task="http://www.springframework.org/schema/task"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
           " default-lazy-init="true">
        <context:component-scan base-package="com.event"/>
    
        <!-- 任务调度器 -->
        <task:scheduler id="scheduler"  pool-size="5"/>
    
        <!-- 任务执行器 -->
        <task:executor id="payment-status-update-thread" pool-size="10"/>
    
        <!--开启注解调度支持 @Async @Scheduled-->
        <task:annotation-driven executor="payment-status-update-thread" scheduler="scheduler" proxy-target-class="true"/>
    </beans>

    在监听器中的使用

    /**
     * Created by zhurudong on 2017/10/22.
     */
    @Component
    public class StockPaymentStatusUpdateListener implements ApplicationListener<PaymentStatusUpdateEvent> {
    
        @Async
        @Override
        public void onApplicationEvent(PaymentStatusUpdateEvent event) {
            System.out.println("库存服务, 收到支付状态更新的事件. " + event + " - Thread: " + Thread.currentThread().getName());
        }
    }

    通过上面的注解@Async便可方便地将事件监听器异步化,没有加这个注解的监听器仍然在发布事件的线程中执行。

    我们看看执行的结果:

    通过上面的执行结果我们可以知道,邮件服务和短信服务因为没有加@Async注解,看到监听器的执行线程是在发布线程中运行的。而加了@Async注解的库存服务器执行线程是在spring配置文件中配置的执行器

    2、原理解析

    先来了解下Spring事件驱动模型的接口UML图:

    从这个图中我们可以大致的了解Spring事件驱动中的各个组件是如何协同一起完成事件的触发和事件的执行。ApplicationListener作为Spring事件驱动中的监听器基类接口,要想实现一个由Spring驱动的事件监听,可以继承于该类;而ApplicationEventPublisher定义了事件的发布接口,Spring容器ApplicatonContext继承了该类,并实现了如何将是一个事件对象ApplicationEvent发布,并通知到事件监听器。先看分析下面几个比较重要的类:

    1. ApplicationEvent: 事件对象,Spring事件驱动模型中的对象源,继承JDK EventObject,通过在发布事件时通过EventObject.source字段携带事件相关的数据。

    2. ApplicationListener: 应用监听器,负责监听事件对象是否有发生变化,实现该接口并实现onApplicationEvent方法,完成事件发生变化时的逻辑处理

    3. ApplicationEventPublisher: 事件发布器,定义了事件发布规范,只定义了接口,具体的实现交由其他类中实现。Spring提供了SimpleApplicationEventMulticaster实现了广播事件发布器。

    理解Spring的事件模型可以从上的类图中,清晰地看到各个组件之间的合作关系,并将合作关系的共性抽象出来,对观察者模式做了抽象。

    下文我们会从事件驱动模型的各个构件出发分析Spring的事件驱动模型,包含事件对象、事件源(发布事件者)、事件监听器。

    1、 事件(事件对象)

    Spring事件驱动模型中,承担事件对象的类是ApplicationEvent,集成JDK提供的事件对象EventObject.

    此类为Spring事件中用于扩展的事件对象,所有需要使用事件驱动模型发布的事件都可以继承此类,并将事件对象Object source;传递给事件监听器使用,通过该source可以得到事件源,监听器拿到事件源后便可以处理自己的业务逻辑。

    public abstract class ApplicationEvent extends EventObject {
    
       /** use serialVersionUID from Spring 1.2 for interoperability */
       private static final long serialVersionUID = 7099057708183571937L;
    
       /** System time when the event happened */
       private final long timestamp;
    
       public ApplicationEvent(Object source) {
          super(source);
          this.timestamp = System.currentTimeMillis();
       }
    
       public final long getTimestamp() {
          return this.timestamp;
       }
    }

    Spring自身的实现中,有很多地方用到了事件它自己定义的事件驱动模型。ApplicationContextEvent就是Spring容器事件的基类。

    public abstract class ApplicationContextEvent extends ApplicationEvent {
    
       public ApplicationContextEvent(ApplicationContext source) {
          super(source);
       }
    
       public final ApplicationContext getApplicationContext() {
          return (ApplicationContext) getSource();
       }
    }

    例如Spring中基于ApplicationContextEvent实现的事件如图所示:

    ContextStartedEvent:Spring Application容器启动后触发的事件。
    ContextStopedEvent:Spring Application容器停止后触发的事件。
    ContextClosedEvent:Spring Application容器关闭后触发的事件。
    ContextRefreshedEvent:Spring Application容器初始化或者刷新完成后触发的事件。

    2、 事件源-发布事件者

    有了上面的事件对象后,如何发布事件对象呢?同样,Spring也提供了事件发布器,ApplicationContext继承了ApplicationEventPublisher,也就是说明Application有了事件发布的功能。

    public abstract class AbstractApplicationContext extends DefaultResourceLoader
          implements ConfigurableApplicationContext, DisposableBean {
    
    // 省略其他代码
        public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
    
        @Override
        public void publishEvent(ApplicationEvent event) {
            publishEvent(event, null);
        }
    
        @Override
        public void publishEvent(Object event) {
            publishEvent(event, null);
        }      
        protected void publishEvent(Object event, ResolvableType eventType) {
            Assert.notNull(event, "Event must not be null");
            if (logger.isTraceEnabled()) {
                logger.trace("Publishing event in " + getDisplayName() + ": " + event);
            }
    
            // Decorate event as an ApplicationEvent if necessary
            ApplicationEvent applicationEvent;
            if (event instanceof ApplicationEvent) {
                applicationEvent = (ApplicationEvent) event;
            } else {
                applicationEvent = new PayloadApplicationEvent<Object>(this, event);
                if (eventType == null) {
                    eventType = ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass());
                }
            }
    
            // Multicast right now if possible - or lazily once the multicaster is initialized
            if (this.earlyApplicationEvents != null) {
                this.earlyApplicationEvents.add(applicationEvent);
            } else {
                getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
            }
    
            // Publish event via parent context as well...
            if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }
    1). 事件发布器是如何初始化

    先来看AbstractApplicationContext中定义的一个常量:

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    该行代码会根据Spring bean容器byName的方式初始化一个事件广播发布器,所以在自定义事件发布器时,需要将bean的id设置为applicationEventMulticaster,这时才能将自定义的事件发布器注入。

    例如如下代码:

    <bean id="applicationEventMulticaster" class="xx.xx.MySimpleApplicationEventMulticaster">
        <property name="taskExecutor" ref="spring-task-executor"/>
    </bean>

    Spring初始化事件发布器的代码如下:

    protected void initApplicationEventMulticaster() {
       ConfigurableListableBeanFactory beanFactory = getBeanFactory();
       // 先从beanfactory容器中查找,如果有广播事件发布器则直接饮用;如果没有创建默认的SimpleApplicationEventMulticaster广播事件发布器
       if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
          this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
          if (logger.isDebugEnabled()) {
             logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
          }
       }
       else {
          this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
          beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
          if (logger.isDebugEnabled()) {
             logger.debug("Unable to locate ApplicationEventMulticaster with name '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "': using default [" + this.applicationEventMulticaster + "]");
          }
       }
    }

    Spring中定义的事件发布器需要实现ApplicationEventMulticaster该接口,该接口具有广播的功能。

    Spring事件发布器的初始化比较简单。首先从BeanFactory容器中,根据byName的方式查找名称为applicationEventMulticaster的事件发布器,如果bean容器中没有定义,则Spring会为我们初始化一个默认的事发广播发布器SimpleApplicationEventMulticaster,该类实现了ApplicationEventMulticaster接口,初始化后将对象的引用复制this.applicationEventMulticaster,到此事件发布器初始化完毕。

    思考:

    1. 我们仔细观察上面的类图,ApplicationEventPublisher定义了事件发布的基础接口,也就是一个事件发布器应该如何发布。

    2. 具体是如何发布事件的Spring容器并没有直接从该ApplicationEventPublisher接口中去实现,而是从新定义了ApplicationEventMulticaster广播事件发布器,并以对象关联的方式注入到AbstractApplicationContext当中,作为AbstractApplicationContext的成员变量。这种强对象关联的方式,也就强调了所有要使用Spring的事件驱动模型的发布器必须要实现ApplicationEventMulticaster接口。

    3. 幸运的是,Spring已经默认为我们实现了一个广播事件发布器SimpleApplicationEventMulticaster,一般的使用场景里面,直接使用该事件发布器就足够了。有特殊需求如需要设置全局异步模式,可以将SimpleApplicationEventMulticaster初始化,设置taskExecutor属性即可,具体如何使用上文有提到。

    2). 事件时如何发布
    @Override
    public void publishEvent(ApplicationEvent event) {
       publishEvent(event, null);
    }
    
    @Override
    public void publishEvent(Object event) {
       publishEvent(event, null);
    }
    
    protected void publishEvent(Object event, ResolvableType eventType) {
    }

    从上述的源码中可以看出,最终需要发布事件的方法是AbstractApplicationContext#publishEvent(Object event, ResolvableType eventType); publishEvent接受一个Object和ResolvableType做为参数发布事件对象。

    第二个参数ResolvableType主要用于区分发布的事件类型,对不同的事件可以根据Object和ResolvableType作为一个key从监听器列表中筛选出可以执行任务的监听器出来,具体如何筛选下文再分析。

    回过头看看事件时如何发布的:

    1. 将Object转成ApplicationEvent事件对象。如果不是ApplicationEvent的子类,则通过PayloadApplicationEvent包装,该类继承了ApplicationEvent

    2. 获取广播事件发布器AbstractApplication#getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); 发布事件

    3. 判断ApplicationContext上下文是否存在IOC父容器,如果存在调用父容器的publishEvent(event)发布事件。这样IOC子容器发布的事件父容器也能感知得到。

    事件的发布源码如下:

    protected void publishEvent(Object event, ResolvableType eventType) {
       Assert.notNull(event, "Event must not be null");
       if (logger.isTraceEnabled()) {
          logger.trace("Publishing event in " + getDisplayName() + ": " + event);
       }
    
       // 1 将object转成ApplicationEvent
       ApplicationEvent applicationEvent;
       if (event instanceof ApplicationEvent) {
          applicationEvent = (ApplicationEvent) event;
       }
       else {
          applicationEvent = new PayloadApplicationEvent<Object>(this, event);
          if (eventType == null) {
             eventType = ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass());
          }
       }
    
       // 2. 获取广播事件发布器,进行事件发布
       if (this.earlyApplicationEvents != null) {
          this.earlyApplicationEvents.add(applicationEvent);
       }
       else {
          getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
       }
    
       // 3. 对当前IOC的父容器发布下事件
       if (this.parent != null) {
          if (this.parent instanceof AbstractApplicationContext) {
             ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
          }
          else {
             this.parent.publishEvent(event);
          }
       }
    }
    3). 事件发布总结

    到此,我们已经把如何发布一个事件对象说完了。ApplicationContext通过继承ApplicationEventPublisher让Spring应用上下文有了发布事件的能力,并通过publishEvent接口将一个事件源ApplicationEvent,通过具体事件发布器的具体实现类SimpleApplicationEventMulticaster将事件发布出去。

    到这里,好像还有东西没有说完。没错,当事件发布出去后,监听器是如何监听到的,并且如何筛选出合适的监听器来执行任务的呢?接下来就应该是监听器登场的时刻

    3、 事件监听器

    我们先来看看事件监听的继承结构:

    从上图我们可以看出,监听的继承结构比较简单,统一继承了JDK定义的EventListenerApplicationListner作为Spring事件驱动模型的监听器基类。

    如果要实现一个监听器,可以实现ApplicationListener接口,然后重写onApplicationEvent(E event);方法。

    那么当一个事件发布后,如何通知到监听器去执行任务,监听器是如何感知到有事件发布呢,下面我们来聊聊事件监听器的筛选过程和事件监听器是如何执行的。

    1). 检索合适事件监听器

    筛选原则
    根据ApplicationEvent中的source class类型进行筛选。

    前面我们聊过一个事件是通过

    getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); 

    这行代码被发布出去的。我们来看看该SimpleApplicationEventMulticaster#mullticastEvent(applicationEvent, eventType) 是如何对事件监听器进行筛选,筛选出合适的事件监听器。

    @Override
    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
       ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
       // getApplicationListeners该方法筛选出合适的事件监听器
       for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
          Executor executor = getTaskExecutor();
          if (executor != null) {
             executor.execute(new Runnable() {
                @Override
                public void run() {
                   invokeListener(listener, event);
                }
             });
          }
          else {
             invokeListener(listener, event);
          }
       }
    }

    其中,比较重要的是一句是getApplicationListeners(event, type),该方法通过event对象本身和包装了ResolvableType事件解析类型,筛选出合适的监听器。这个方法是在AbstractApplicationEventMulticaster类中实现的,该类还定义了retrieverCacheCurrentHashMap用于缓存加载过的监听器。

    private final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<ListenerCacheKey, ListenerRetriever>(64);

    getApplicationListeners(event, type)方法执行步骤如下:

    1.构建ListenerCacheKey, 从缓存retrieverCache中获取事件事件监听器,如果有则直接调用retriever.getApplicationListeners(); 返回事件监听器
    
    2.AbstractApplicationEventMulticaster#retrieveApplicationListeners(ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever),调用此方式根据sourceType检索。
    sourceType是event.getSource(),也就是事件对象里面的source对象源
    
    3.将上一步检索到的监听器添加到retrieverCache缓存中,此过程中是synchronized

    至于上面第2步中是如何检索?Spring在容器初始化时,相应的factoryBean信息已经在beanFactory Spring工厂中构建好。在执行上面第2步时,会调用

    ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); 

    进行初始化factoryBean,并将监听器封装到ListenerRetriever retriever中,后面将retriever添加到retrieveCache中,提供下次检索时直接从缓存中获取,不用再进行bean的初始化。

    2). 事件监听器执行

    通过上个步骤根据event.getSource()的Class类型检索到对应的Listener,到事件监听器的执行就非常容易了。

    分两步执行:

    1. 判断applicationEventMulticaster初始化广播事件发布器时,有没有对该taskExecutor属性进行赋值。如果有采用线程池中的线程执行AbstractApplicationEventMulticaster#invokeListener(listener, event)

    2. 如果没有设置线程池,进行同步执行。由发布事件的线程执行

    invokeListener方法中,直接调用listener.onApplicationEvent(event)便可执行事件。

    到此事件驱动模型已经分析完毕,本文撰写跨越的时间周期比较长,希望通过此文能把观察者模式和Spring事件模型讲解清楚。

    参考资料:

    1. http://blog.csdn.net/gykimo/article/details/9182287
    2. https://baike.baidu.com/item/%E4%BA%8B%E4%BB%B6%E9%A9%B1%E5%8A%A8/9597519?fr=aladdin
    3. http://158067568.iteye.com/blog/901579
    4. http://blog.csdn.net/small__snail/article/details/50115063
    5. http://www.cnblogs.com/fingerboy/p/6393644.html
    6. spring源码阅读
    展开全文
  • SpringBoot整合RabbitMQ之Spring事件驱动模型-系统源码数据库流程图 SpringBoot整合RabbitMQ实战视频教程:https://edu.csdn.net/course/detail/9314 (感兴趣也可以加QQ联系:1974544863)
  • 时滞系统的少关注事件驱动模型预测控制
  • nginx服务器的事件驱动模型

    千次阅读 2017-06-07 20:33:25
    事件驱动模型是Nginx服务器保障完整功能和具有良好性能的重要机制之一。 事件驱动模型一般是由事件收集器、事件发送器、事件处理器三部分基本单元组成。 在nginx里面有三个库,就是大名鼎鼎的select库,poll库,...

    事件驱动模型是Nginx服务器保障完整功能和具有良好性能的重要机制之一。


    事件驱动模型一般是由事件收集器、事件发送器、事件处理器三部分基本单元组成。


    在nginx里面有三个库,就是大名鼎鼎的select库,poll库,epoll库。

    select库

          select库,是linux和windows都支持的基本事件驱动模型。并且在接口上的定义基本相同,只是部分的参数可能有略微的差异。select库的步骤一般是:
          首先,创建所关注事件的描述符集合。对于一个描述符,可以关注其上面的读(Read)事件,写(Write)事件以及异常发生(Exception)事件。所以要创建三类事件描述符集合。分别用来收集读事件,写事件和异常事件的描述符的集合。
          其次,调用底层提供的select函数。等待事件发生。
          然后,轮询所有事件描述符集合中的每一个事件描述符。检查是否有相应的事件发生,如果有,就进行处理。

    poll库

        poll库,作为Linux上的基本事件驱动模型,windows是不支持的。poll库和select库的工作方式基本相同,都是创建一个关注事件的描述符集合。再去等待这些事件的发生。然后再轮询描述符集合。检查是否有相应的事件发生,有就进行处理。
        但是poll和select库的区别是,select需要为读事件,写事件,异常事件分别创建一个描述符,因此在最后轮询的时候,需要分别轮询三个集合。而poll库只需要创建一个集合,在每个集合描述符上分别设置读事件,写事件或者异常事件。在最后的轮询过程中,可以同时检查三种事件是否发生。可以说,poll库是select的优化升级版本。

    epoll库

        epoll库是Nginx服务器支持的高性能事件驱动库之一。它是公认的最好的事件驱动模型。和poll库及select库有很大的区别。
        poll和select都是创建一个待处理事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表。以判断这个事件是否发生。在描述符太多的情况下,就会明显效率低下了。
        epoll是这么做的,它把事件描述符列表的管理交给内核复制。一旦有摸个事件发生,内核将发生事件的事件描述符交给Nginx的进程,而不是将整个事件描述符列表交给进程,让进程去轮询具体是哪个描述符。epoll()避免了轮询整个事件描述符列表。所以显得更高效。
        epoll库的基本步骤:
        首先:epoll库通过相关调用通知内核创建一个有N个描述符的事件列表。饭后给这个事件列表设置自己关心的事件。并把它添加到内核中。在具体的代码中还可以实现对相关调用的事件描述符列表进行修改和删除。
        之后,一旦设置完成就一直等待内核通知事件发生了,某一事件发生后,内核就将发生事件的描述符给epoll库,epoll库去处理事件。

       epoll库在Linux平台上是十分高效的,它支持一个进程打开大数目的事件描述符。上限时可以打开的文件最大数。可以由ulimit -n 和 max_file去设置打开的最大描述符。


    另外select和poll是是水平触发的(Level Triggered)。就是此次描述符的响应未处理的话,下次还可以再次提醒处理
    而epoll是水平触发(Level Triggered)和边沿(Edge Triggered)都支持的。理论上边沿触发的性能更高点


    python实现的select。
    import socket  
    import Queue  
    from select import select  
      
    SERVER_IP = ('127.0.0.1', 9999)  
      
    # 保存客户端发送过来的消息,将消息放入队列中  
    message_queue = {}  
    input_list = []  
    output_list = []  
      
    if __name__ == "__main__":  
        server = socket.socket()  
        server.bind(SERVER_IP)  
        server.listen(10)  
        # 设置为非阻塞  
        server.setblocking(False)  
      
        # 初始化将服务端加入监听列表  
        input_list.append(server)  
      
        while True:  
            # 开始 select 监听,对input_list中的服务端server进行监听  
            stdinput, stdoutput, stderr = select(input_list, output_list, input_list)  
      
            # 循环判断是否有客户端连接进来,当有客户端连接进来时select将触发  
            for obj in stdinput:  
                # 判断当前触发的是不是服务端对象, 当触发的对象是服务端对象时,说明有新客户端连接进来了  
                if obj == server:  
                    # 接收客户端的连接, 获取客户端对象和客户端地址信息  
                    conn, addr = server.accept()  
                    print("Client {0} connected! ".format(addr))  
                    # 将客户端对象也加入到监听的列表中, 当客户端发送消息时 select 将触发  
                    input_list.append(conn)  
                    # 为连接的客户端单独创建一个消息队列,用来保存客户端发送的消息  
                    message_queue[conn] = queue.Queue()  
      
                else:  
                    # 由于客户端连接进来时服务端接收客户端连接请求,将客户端加入到了监听列表中(input_list),客户端发送消息将触发  
                    # 所以判断是否是客户端对象触发  
                    try:  
                        recv_data = obj.recv(1024)  
                        # 客户端未断开  
                        if recv_data:  
                            print("received {0} from client {1}".format(recv_data.decode(), addr))  
                            # 将收到的消息放入到各客户端的消息队列中  
                            message_queue[obj].put(recv_data)  
      
                            # 将回复操作放到output列表中,让select监听  
                            if obj not in output_list:  
                                output_list.append(obj)  
      
                    except ConnectionResetError:  
                        # 客户端断开连接了,将客户端的监听从input列表中移除  
                        input_list.remove(obj)  
                        # 移除客户端对象的消息队列  
                        del message_queue[obj]  
                        print("\n[input] Client  {0} disconnected".format(addr))  
      
            # 如果现在没有客户端请求,也没有客户端发送消息时,开始对发送消息列表进行处理,是否需要发送消息  
            for sendobj in output_list:  
                try:  
                    # 如果消息队列中有消息,从消息队列中获取要发送的消息  
                    if not message_queue[sendobj].empty():  
                        # 从该客户端对象的消息队列中获取要发送的消息  
                        send_data = message_queue[sendobj].get()  
                        sendobj.sendall(send_data)  
                    else:  
                        # 将监听移除等待下一次客户端发送消息  
                        output_list.remove(sendobj)  
      
                except ConnectionResetError:  
                    # 客户端连接断开了  
                    del message_queue[sendobj]  
                    output_list.remove(sendobj)  
                    print("\n[output] Client  {0} disconnected".format(addr))  
    


    python实现的epoll
    #!/usr/bin/env python  
    import select  
    import socket  
      
    response = b''  
      
    serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
    serversocket.bind(('0.0.0.0', 8080))  
    serversocket.listen(1)  
    # 因为socket默认是阻塞的,所以需要使用非阻塞(异步)模式。  
    serversocket.setblocking(0)  
      
    # 创建一个epoll对象  
    epoll = select.epoll()  
    # 在服务端socket上面注册对读event的关注。一个读event随时会触发服务端socket去接收一个socket连接  
    epoll.register(serversocket.fileno(), select.EPOLLIN)  
      
    try:  
        # 字典connections映射文件描述符(整数)到其相应的网络连接对象  
        connections = {}  
        requests = {}  
        responses = {}  
        while True:  
            # 查询epoll对象,看是否有任何关注的event被触发。参数“1”表示,我们会等待1秒来看是否有event发生。  
            # 如果有任何我们感兴趣的event发生在这次查询之前,这个查询就会带着这些event的列表立即返回  
            events = epoll.poll(1)  
            # event作为一个序列(fileno,event code)的元组返回。fileno是文件描述符的代名词,始终是一个整数。  
            for fileno, event in events:  
                # 如果是服务端产生event,表示有一个新的连接进来  
                if fileno == serversocket.fileno():  
                    connection, address = serversocket.accept()  
                    print('client connected:', address)  
                    # 设置新的socket为非阻塞模式  
                    connection.setblocking(0)  
                    # 为新的socket注册对读(EPOLLIN)event的关注  
                    epoll.register(connection.fileno(), select.EPOLLIN)  
                    connections[connection.fileno()] = connection  
                    # 初始化接收的数据  
                    requests[connection.fileno()] = b''  
      
                # 如果发生一个读event,就读取从客户端发送过来的新数据  
                elif event & select.EPOLLIN:  
                    print("------recvdata---------")  
                    # 接收客户端发送过来的数据  
                    requests[fileno] += connections[fileno].recv(1024)  
                    # 如果客户端退出,关闭客户端连接,取消所有的读和写监听  
                    if not requests[fileno]:  
                        connections[fileno].close()  
                        # 删除connections字典中的监听对象  
                        del connections[fileno]  
                        # 删除接收数据字典对应的句柄对象  
                        del requests[connections[fileno]]  
                        print(connections, requests)  
                        epoll.modify(fileno, 0)  
                    else:  
                        # 一旦完成请求已收到,就注销对读event的关注,注册对写(EPOLLOUT)event的关注。写event发生的时候,会回复数据给客户端  
                        epoll.modify(fileno, select.EPOLLOUT)  
                        # 打印完整的请求,证明虽然与客户端的通信是交错进行的,但数据可以作为一个整体来组装和处理  
                        print('-' * 40 + '\n' + requests[fileno].decode())  
      
                # 如果一个写event在一个客户端socket上面发生,它会接受新的数据以便发送到客户端  
                elif event & select.EPOLLOUT:  
                    print("-------send data---------")  
                    # 每次发送一部分响应数据,直到完整的响应数据都已经发送给操作系统等待传输给客户端  
                    byteswritten = connections[fileno].send(requests[fileno])  
                    requests[fileno] = requests[fileno][byteswritten:]  
                    if len(requests[fileno]) == 0:  
                        # 一旦完整的响应数据发送完成,就不再关注写event  
                        epoll.modify(fileno, select.EPOLLIN)  
      
                # HUP(挂起)event表明客户端socket已经断开(即关闭),所以服务端也需要关闭。  
                # 没有必要注册对HUP event的关注。在socket上面,它们总是会被epoll对象注册  
                elif event & select.EPOLLHUP:  
                    print("end hup------")  
                    # 注销对此socket连接的关注  
                    epoll.unregister(fileno)  
                    # 关闭socket连接  
                    connections[fileno].close()  
                    del connections[fileno]  
    finally:  
        # 打开的socket连接不需要关闭,因为Python会在程序结束的时候关闭。这里显式关闭是一个好的代码习惯  
        epoll.unregister(serversocket.fileno())  
        epoll.close()  
        serversocket.close()  


    展开全文
  • 基于事件驱动模型的搜索引擎设计 基于事件驱动模型的搜索引擎设 摘要介绍了搜索引擎个性化服务的主要技术结合离散事件驱动模型的思想DWR 和 现有搜索引擎的基本架构设计了基于事件驱动模型的搜索引擎的系统结构提出...
  • Spring3.2.6中事件驱动模型实现原理深入源码分析本次学习,是在新入公司熟悉项目时候开始的。因为是做页游的项目,所以涉及到gameServer做会将游戏中的业务操作日志交给logServer处理。在本次项目中是采用spring自带...

    Spring3.2.6中事件驱动模型实现原理深入源码分析

    本次学习,是在新入公司熟悉项目时候开始的。因为是做页游的项目,所以涉及到gameServer做会将游戏中的业务操作日志交给logServer处理。在本次项目中是采用spring自带的事件驱动模型(Event-Listener)来完成的,所以就花时间深入spring和jdk源码好好分析了其实现原理,收获颇多,故在此记录,留做以后复习查看。

    什么叫事件驱动模型?

    要了解什么是事件驱动模型,首先要知道下面这样几个概念,我以JDK中GUI的按钮点击做说明。
    
    1. 事件(Event): 用户点击按钮(Button),就会产生事件(Event)。这里就是点击事件(ClickEvent)。
    2. 事件源(EventSource):发生事件的根源(场所)就是事件源。这里按钮组件就是事件源。
    3. 事件监听器(EventListener):监听特定的事件并处理的程序。这里监听器就是点击事件的处理者。
    4. 事件分发者(EventDispatcher):将发生的事件通知相对应的事件处理者的程序。这里就是通知监听器处理事件的程序。
    import java.awt.Dimension;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JOptionPane;
    import javax.swing.WindowConstants;
    
    
    /** 
     * @Description: JDK中GUI按钮的点击事件演示
     * @author Yang Gao 
     * @date 2016-3-28 上午9:48:42 
     *  
     */
    public class EventListenerTest {
    
        public static void main(String[] args) {
            Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
            //当前屏幕中心点的X坐标
            final int CONTENT_X = d.width/2;
            //当前屏幕中心点的Y坐标
            final int CONTENT_Y = d.height/2;
    
            //Windos窗口组件
            JFrame f = new JFrame("测试窗口");
            //窗口组件大小
            f.setSize(300, 200);
            //窗口组件位置
            f.setLocation((CONTENT_X - f.getWidth()/2), (CONTENT_Y - f.getHeight()/2));
            //设置大小不可改变
            f.setResizable(false);
            //设置可见性为可见
            f.setVisible(true);
            //设置默认关闭调用事件
            f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            //按钮组件,事件源
            JButton btn = new JButton("点击我哟!");
            //设置按钮组件的大小
            btn.setSize(150, 50);
            //面板添加组件
            f.add(btn);
            //对事件源添加事件监听器
            btn.addActionListener(new MyListener());
        }
    
    }
    
    /**
     * @Description: 自定义clickListenert类
     * @author Yang Gao 
     * @date 2016-3-28 上午10:17:16 
     *
     */
    class MyListener implements ActionListener{
    
        @Override
        public void actionPerformed(ActionEvent e) {
            JButton btn = (JButton)e.getSource();
            JOptionPane.showMessageDialog(null,"我是事件监听器的处理程序,我处理的是:【" + btn.getText() + "】"); 
        }
    
    }
    

    在上面的code中btn.addActionListener(new MyListener());至于jdk是如何将用户产生的click事件通知给MyListener,也就是事件发布者这个角色,这里不详细叙述,有兴趣的童鞋可以考虑跟进jdk源码来找到答案。

    Spring是如何实现?

    这里写图片描述
    1.先看看所有事件Event类都要继承的父类EventObject(JDK)代码。

    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 + "]";
        }
    }
    

    再看看Spring中继承EventObject的类,ApplicationEvent的具体代码

    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context;
    
    import java.util.EventObject;
    
    /**
     * Class to be extended by all application events. Abstract as it
     * doesn't make sense for generic events to be published directly.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     */
    public abstract class ApplicationEvent extends EventObject {
    
        /** use serialVersionUID from Spring 1.2 for interoperability */
        private static final long serialVersionUID = 7099057708183571937L;
    
        /** System time when the event happened */
        private final long timestamp;
    
    
        /**
         * Create a new ApplicationEvent.
         * @param source the component that published the event (never {@code null})
         */
        public ApplicationEvent(Object source) {
            super(source);
            this.timestamp = System.currentTimeMillis();
        }
    
    
        /**
         * Return the system time in milliseconds when the event happened.
         */
        public final long getTimestamp() {
            return this.timestamp;
        }
    
    }
    

    2.接下来,我们看看所有事件监听器Listener类都要实现的EventListener(JDK)接口。

    /*
     * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     */
    
    package java.util;
    
    /**
     * A tagging interface that all event listener interfaces must extend.
     * @since JDK1.1
     */
    public interface EventListener {
    }
    

    那么Spring中实现了这个接口的是接口ApplicationListener< E extends ApplicationEvent > ,很明显使用的泛型,约定了其事件类型,看看具体代码。

    package org.springframework.context;
    
    import java.util.EventListener;
    
    /**
     * Interface to be implemented by application event listeners.
     * Based on the standard {@code java.util.EventListener} interface
     * for the Observer design pattern.
     *
     * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
     * that it is interested in. When registered with a Spring ApplicationContext, events
     * will be filtered accordingly, with the listener getting invoked for matching event
     * objects only.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     * @param <E> the specific ApplicationEvent subclass to listen to
     * @see org.springframework.context.event.ApplicationEventMulticaster
     */
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
        /**
         * Handle an application event.
         * @param event the event to respond to
         */
        void onApplicationEvent(E event);
    
    }
    

    从上面可以看出,当泛型约定的事件发生时候,会调用其onApplicationEvent的实现方法处理。我们再看看spring中实现这一接口的子接口:SmartApplicationListener的具体代码。

    
    package org.springframework.context.event;
    
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.core.Ordered;
    
    /**
     * Extended variant of the standard {@link ApplicationListener} interface,
     * exposing further metadata such as the supported event type.
     *
     * @author Juergen Hoeller
     * @since 3.0
     */
    public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    
        /**
         * Determine whether this listener actually supports the given event type.
         */
        boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
    
        /**
         * Determine whether this listener actually supports the given source type.
         */
        boolean supportsSourceType(Class<?> sourceType);
    
    }
    

    从上面的代码上可以看出,这个子接口添加了更为具体的类型判断,supportsEventType表示仅支持对应的事件类型,supportsSourceType表示仅支持对应的事件类型(这里就对应EventObject类中被transient瞬态修饰符的变量Object source)。

    3.最后我们再看看最核心的东东,就是事件分发器(EventDispatcher)。Spring中使用接口ApplicationEventPublisher和接口ApplicationEventMulticaster来共同完成,首先我们来看一下两个接口定义的通用方法有哪些?

    package org.springframework.context;
    public interface ApplicationEventPublisher {
        /**
         * Notify all listeners registered with this application of an application
         * event. Events may be framework events (such as RequestHandledEvent)
         * or application-specific events.
         * @param event the event to publish
         * @see org.springframework.web.context.support.RequestHandledEvent
         */
        void publishEvent(ApplicationEvent event);
    }
    package org.springframework.context.event;
    
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    
    public interface ApplicationEventMulticaster {
    
        /**
         * Add a listener to be notified of all events.
         * @param listener the listener to add
         */
        void addApplicationListener(ApplicationListener listener);
    
        /**
         * Add a listener bean to be notified of all events.
         * @param listenerBeanName the name of the listener bean to add
         */
        void addApplicationListenerBean(String listenerBeanName);
    
        /**
         * Remove a listener from the notification list.
         * @param listener the listener to remove
         */
        void removeApplicationListener(ApplicationListener listener);
    
        /**
         * Remove a listener bean from the notification list.
         * @param listenerBeanName the name of the listener bean to add
         */
        void removeApplicationListenerBean(String listenerBeanName);
    
        /**
         * Remove all listeners registered with this multicaster.
         * <p>After a remove call, the multicaster will perform no action
         * on event notification until new listeners are being registered.
         */
        void removeAllListeners();
    
        /**
         * Multicast the given application event to appropriate listeners.
         * @param event the event to multicast
         */
        void multicastEvent(ApplicationEvent event);
    
    }
    

    从上面可以很清楚的看出,其中ApplicationEventPublisher 接口主要负责将事件发布,ApplicationEventMulticaster 接口主要负责事件通知(回调)。Spring中ApplicationContext接口实现了ApplicationEventPublisher 接口,具体实现类是AbstractApplicationContext,让我们看看源代码中对publishEvent实现的具体代码。

    
        /**
         * Publish the given event to all listeners.
         * <p>Note: Listeners get initialized after the MessageSource, to be able
         * to access it within listener implementations. Thus, MessageSource
         * implementations cannot publish events.
         * @param event the event to publish (may be application-specific or a
         * standard framework event)
         */
        public void publishEvent(ApplicationEvent event) {
            Assert.notNull(event, "Event must not be null");
            if (logger.isTraceEnabled()) {
                logger.trace("Publishing event in " + getDisplayName() + ": " + event);
            }
            getApplicationEventMulticaster().multicastEvent(event);
            if (this.parent != null) {
                this.parent.publishEvent(event);
            }
        }
    /**
         * Return the internal ApplicationEventMulticaster used by the context.
         * @return the internal ApplicationEventMulticaster (never {@code null})
         * @throws IllegalStateException if the context has not been initialized yet
         */
        private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
            if (this.applicationEventMulticaster == null) {
                throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
                        "call 'refresh' before multicasting events via the context: " + this);
            }
            return this.applicationEventMulticaster;
        }
    

    大家应该发现了,就和我画的图一样,具体实现的是接口ApplicationEventMulticaster 。那么ApplicationEventMulticaster 接口的具体实现代码是什么呢?我们接着跟进,看看实现ApplicationEventMulticaster 接口中multicastEvent方法的具体类SimpleApplicationEventMulticaster的源代码。

    package org.springframework.context.event;
    import java.util.concurrent.Executor;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    
    public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    
        private Executor taskExecutor;
    
        /**
         * Create a new SimpleApplicationEventMulticaster.
         */
        public SimpleApplicationEventMulticaster() {
        }
    
        /**
         * Create a new SimpleApplicationEventMulticaster for the given BeanFactory.
         */
        public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
            setBeanFactory(beanFactory);
        }
    
    
        /**
         * Set the TaskExecutor to execute application listeners with.
         * <p>Default is a SyncTaskExecutor, executing the listeners synchronously
         * in the calling thread.
         * <p>Consider specifying an asynchronous TaskExecutor here to not block the
         * caller until all listeners have been executed. However, note that asynchronous
         * execution will not participate in the caller's thread context (class loader,
         * transaction association) unless the TaskExecutor explicitly supports this.
         * @see org.springframework.core.task.SyncTaskExecutor
         * @see org.springframework.core.task.SimpleAsyncTaskExecutor
         */
        public void setTaskExecutor(Executor taskExecutor) {
            this.taskExecutor = taskExecutor;
        }
    
        /**
         * Return the current TaskExecutor for this multicaster.
         */
        protected Executor getTaskExecutor() {
            return this.taskExecutor;
        }
    
    
        @SuppressWarnings("unchecked")
        public void multicastEvent(final ApplicationEvent event) {
            for (final ApplicationListener listener : getApplicationListeners(event)) {
                Executor executor = getTaskExecutor();
                if (executor != null) {
                    executor.execute(new Runnable() {
                        public void run() {
                            listener.onApplicationEvent(event);
                        }
                    });
                }
                else {
                    listener.onApplicationEvent(event);
                }
            }
        }
    
    }
    

    问题的关键到了,就是这个方法getApplicationListeners(final ApplicationEvent event),从方法的定义上就不难看出,这个方法是根据事件得到其所有监听器的集合。让我们继续跟进AbstractApplicationEventMulticaster类中的getApplicationListeners这个方法查看里面的奥秘。

    /**
         * Return a Collection of ApplicationListeners matching the given
         * event type. Non-matching listeners get excluded early.
         * @param event the event to be propagated. Allows for excluding
         * non-matching listeners early, based on cached matching information.
         * @return a Collection of ApplicationListeners
         * @see org.springframework.context.ApplicationListener
         */
        protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
            // 事件字节码(这里我们就是zhangsanEvent和WangwuEvent两个事件)
            Class<? extends ApplicationEvent> eventType = event.getClass();
            // 事件源信息的字节码(这里就是我们定义的两个事件的区别:eventName构造方法里面的source)
            Object source = event.getSource();      
            Class<?> sourceType = (source != null ? source.getClass() : null);
            // ListenerCacheKey 保存上面的两个字节码信息
            ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
            // Map<ListenerCacheKey, ListenerRetriever> retrieverCache将匹配过的事件与监听器对应关系保存在内种中作为缓存
            ListenerRetriever retriever = this.retrieverCache.get(cacheKey);// 这里是从内存的缓存中查找
            if (retriever != null) {
                return retriever.getApplicationListeners();
            }
            else {
                // 第一次没有找到,就自己new一个ListenerRetriever来保存返回数据
                retriever = new ListenerRetriever(true);
                LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
                Set<ApplicationListener> listeners;
                Set<String> listenerBeans;
                /**
                * defaultRetriever默认监听器
                * 在applicationContext容器初始化的时候就已经保存了
                * 所有实现ApplicationListener或SmartApplicationListener接口listeners
                * /
                synchronized (this.defaultRetriever) {
                    listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
                    listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
                }
                //遍历所有实现了ApplicationListener或SmartApplicationListener接口listeners
                for (ApplicationListener listener : listeners) {
                    //supportsEvent(根据eventType和source,从listeners中筛选出符合条件的listener)
                    if (supportsEvent(listener, eventType, sourceType)) {
                        retriever.applicationListeners.add(listener);
                        allListeners.add(listener);
                    }
                }
                if (!listenerBeans.isEmpty()) {
                    BeanFactory beanFactory = getBeanFactory();
                    for (String listenerBeanName : listenerBeans) {
                        ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                            retriever.applicationListenerBeans.add(listenerBeanName);
                            allListeners.add(listener);
                        }
                    }
                }
                OrderComparator.sort(allListeners);
                this.retrieverCache.put(cacheKey, retriever);
                return allListeners;
            }
        }

    在上面代码中有一个狠重要的方法supportsEvent,这个方法我说过是SmartApplicationListener接口定义的。这里如果用户自定义listener是直接实现该接口,那么这里就会调用用户自己的现实类;否则就会调用spring默认的,具体我们看抽象类AbstractApplicationEventMulticaster中supportsEvent方法的源代码说话。

        /**
         * Determine whether the given listener supports the given event.
         * <p>The default implementation detects the {@link SmartApplicationListener}
         * interface. In case of a standard {@link ApplicationListener}, a
         * {@link GenericApplicationListenerAdapter} will be used to introspect
         * the generically declared type of the target listener.
         * @param listener the target listener to check
         * @param eventType the event type to check against
         * @param sourceType the source type to check against
         * @return whether the given listener should be included in the
         * candidates for the given event type
         */
        protected boolean supportsEvent(
                ApplicationListener listener, Class<? extends ApplicationEvent> eventType, Class sourceType) {
            /**
             * 从这行代码可以看出来,如果传进来的listener实现SmartApplicationListener的类,
             * 那么就会调用其实现类的supportsEventType和supportsSourceType方法;
             * 否则就会调用GenericApplicationListenerAdapter的supportsEventType和supportsSourceType方法
             * /
            SmartApplicationListener smartListener = (listener instanceof SmartApplicationListener ?
                    (SmartApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
            return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
        }

    好,我们接着跟进查看Spring自己实现SmartApplicationListener接口的类GenericApplicationListenerAdapter,它的源代码。

    
    package org.springframework.context.event;
    
    import org.springframework.aop.support.AopUtils;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.core.GenericTypeResolver;
    import org.springframework.core.Ordered;
    import org.springframework.util.Assert;
    
    public class GenericApplicationListenerAdapter implements SmartApplicationListener {
    
        private final ApplicationListener delegate;
    
    
        public GenericApplicationListenerAdapter(ApplicationListener delegate) {
            Assert.notNull(delegate, "Delegate listener must not be null");
            this.delegate = delegate;
        }
    
    
        @SuppressWarnings("unchecked")
        public void onApplicationEvent(ApplicationEvent event) {
            this.delegate.onApplicationEvent(event);
        }
        // 这里是仅支持事件调用方法的实现
        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
            //这里是获取listener中泛型定义的ApplicationEvent中指定delegate的字节码信息
            Class<?> typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class);
            // 判断是否是ApplicationEvent类
            if (typeArg == null || typeArg.equals(ApplicationEvent.class)) {
                // 获取指定类继承的父类
                Class<?> targetClass = AopUtils.getTargetClass(this.delegate);
                if (targetClass != this.delegate.getClass()) {
                    // 这里是获取listener中泛型定义的ApplicationEvent中指定targetClass的字节码信息
                    typeArg = GenericTypeResolver.resolveTypeArgument(targetClass, ApplicationListener.class);
                }
            }
            /**
            * isAssignableFrom用来判断一个类Class1和另一个类Class2是否相同
            * 或者一个类class1是另一个类class2的超类或接口
            * 注意调用顺序是class1.isAssignableFrom(class2)
            */
            // null 表示没有泛型为任意类型
            return (typeArg == null || typeArg.isAssignableFrom(eventType));
        }
        // 这里是仅支持事件源信息调用方法的实现
        public boolean supportsSourceType(Class<?> sourceType) {
            return true;
        }
    
        public int getOrder() {
            return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
        }
    
    }
    

    好了,到了这里大家基本也都知道这份分发器原理了,这里说的主要是通过supportsEventType和supportsSourceType这两个方法,来判断什么样的ApplicationEvent用什么样的ApplicationListener来处理了。

    Spring两种事件监听方式的实现示例?

    1.首先,我自定义一个我们自己的事件父类IEvent

    
    import org.springframework.context.ApplicationEvent;
    
    /** 
     * @Description: 自定义事件父类,根据事件名称区别
     * @author Yang Gao 
     * @date 2016-3-25 下午4:36:20 
     *  
     */
    public class IEvent extends ApplicationEvent {
    
        private static final long serialVersionUID = -1130255953722074726L;
    
        private String eventName; 
    
    
        public IEvent(String eventName) {
            super(eventName);
            this.eventName = eventName;
        }
    
        public String getEventName() {
            return eventName;
        }
    
    
    }

    2.然后在再定义两个不同的事件类,ZhangsanEvent和WangwuEvent

    package com.gy.event;
    /** 
     * @author Yang Gao 
     * @date 2016-3-25 下午4:44:57 
     *  
     */
    public class ZhangsanEvent extends IEvent { 
    
        private static final long serialVersionUID = 4328148476272415442L;
    
        public ZhangsanEvent(String source) {
            super(source);
        }
    }
    package com.gy.event;
    /**  
     * @author Yang Gao
     * @date 2016-3-25 下午4:44:57 
     *  
     */
    public class WangwuEvent extends IEvent {   
    
        private static final long serialVersionUID = 4328148476272415442L;
    
        public WangwuEvent(String source) {
            super(source);
        }
    }

    好了,我们来看一下具体的继承关系。

    这里写图片描述

    3.然后我们在定义自己的Listenter,这里我使用Spring的两种实现方式

    package com.gy.listener;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.event.SmartApplicationListener;
    import org.springframework.stereotype.Component;
    import com.gy.event.ZhangsanEvent;
    /** 
     * @Description: 使用实现间接实现ApplicationListener的子接口SmartApplicationListener方式
     * @author Yang Gao 
     * @date 2016-3-25 下午4:42:26 
     *  
     */
    public class ZhangsanListener implements SmartApplicationListener {
    
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            ZhangsanEvent _event = (ZhangsanEvent)event;
            System.out.println("ZhangsanListener Handle Event Name is " + _event.getEventName());
    
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    
        /* 
         * 决定监听器处理支持的事件类型
         */
        @Override
        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
            return ZhangsanEvent.class.isAssignableFrom(eventType);
        }
    
        /* 
         * 决定监听器处理支持的事件源类型
         */
        @Override
        public boolean supportsSourceType(Class<?> sourceType) {
            return Object.class.isAssignableFrom(sourceType);
        }
    }
    
    package com.gy.listener;
    
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    
    import com.gy.event.WangwuEvent;
    
    /** 
     * @Description: 使用直接实现ApplicationListener方式,泛型指定监听的具体事件类型
     * @author Yang Gao 
     * @date 2016-3-25 下午4:43:56 
     *  
     */
    public class WangwuListener implements ApplicationListener<WangwuEvent> {
    
        @Override
        public void onApplicationEvent(WangwuEvent event) {
            System.out.println("WangwuListener Handle Event Name is " + event.getEventName());
        }
    
    }
    

    4.然后咱在弄一个测试类SpringEventListenerTest

    package com.gy.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.stereotype.Component;
    
    import com.gy.event.WangwuEvent;
    import com.gy.event.ZhangsanEvent;
    
    /** 
     * @Description: 测试类
     * @author Yang Gao 
     * @date 2016-3-25 下午4:35:30 
     *  
     */
    
    public class SpringEventListenerTest {
    
    
        public static void main(String[] args) {
            ApplicationContext springContext = new ClassPathXmlApplicationContext("applicationContext.xml");
            ZhangsanEvent zsEvent = new ZhangsanEvent("张三");
            WangwuEvent wwEvent = new WangwuEvent("王五");
            springContext.publishEvent(zsEvent);
            springContext.publishEvent(wwEvent);        
    
        }
    
    }
    

    5.最后简单的applicationContext.xml配置文件,当然至于spring3.2.6的jar包,大家都自己动手去网上下载啦,我就不粘了。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:util="http://www.springframework.org/schema/util">
    
    
        <bean id="zhangsanListener" class="com.gy.listener.ZhangsanListener"/>
        <bean id="wangwuListener" class="com.gy.listener.WangwuListener"/>
    
    
    </beans>

    6.好了,run一下,就结束了,让我们来看下结果。
    这里写图片描述

    补充

    总之,这次学习,由于时间比较充足,所以也特意花时间来写博客记录下来供以后复习用。当然,收获也是挺多的,包括在写博客回顾一遍也有收获。另外,这在提醒自己,以后有时间了要去看看Spring加载ApplicaitonContext的全过程,这样也会加深自己对Ioc和Aop的理解,也能更进一步的学习JDK的反射(Reflect)机制和事件驱动模型。


    展开全文
  • js事件驱动模型图解

    2018-04-28 20:47:55
  • 事件驱动模型基本结构

    千次阅读 2018-04-30 20:26:30
    事件驱动模型基本结构 事件驱驱动架构由三个基本组件构成,事件事件处理器、事件循环。事件产生后发送给事件循环,事件循环将每个事件分派给个各个事件处理器。事件A由处理器A处理,事件B将被处理器B处理。 ...
  • 异步事件驱动模型在嵌入式系统中的应用、电子技术,开发板制作交流
  • 观察者模式是软件设计模式的一种。此种模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并自动更新。该模式通常被用来实现事件驱动处理系统。
  • 在分析电荷泵锁相环结构和原理的基础上,采用符号函数sign()来描述状态变化,建立一个输入参考频率为50 MHz,输出频率为900 MHz的三阶电荷泵锁相环的事件驱动模型,通过设定模型中的参数,应用Matlab对模型进行仿真...
  • 事件驱动模型事件监听机制观察者模式案例1.Spring事件机制(事件监听机制)案例2.基于Springboot事件监听机制整合EventBus应用案例3. 事件监听机制 熟悉Spring的同学,spring提供了一套完整的事件监听机制。要了解...
  • 事件驱动模型实例详解(Java篇)
  • Java 开发中事件驱动模型的实例详解,属于转的
  • 实战背景:在进入RabbitMQ各大技术知识点之前,我们先来谈谈跟事件驱动息息相关的ApplicationEvent、ApplicationListener以及ApplicationEventPublisher这三大组件,点击进去看其源码可以发现里面使用的...
  • 提出了基于约束规则集的事件驱动模型的概念,以之为基础并结合确定性有穷自动机的理论,建立了整个集装箱物流的工作流程,通过满足约束规则的事件来驱动物流和资金流的状态改变,并给出了系统的一个具体实现方案。
  • 基于事件驱动模型构建可扩展的物联网服务
  • Spring观察者驱动模型几个关键的类 ApplicationEvent:事件 ApplicationListener:事件监听器,方法void onApplicationEvent(E event) ApplicationEventPublisher:事件发布器,方法void publishEvent...
  • nodejs的事件驱动模型

    千次阅读 2016-12-21 15:55:39
    nodejs是单进程单线程,但是基于V8的强大驱动力,以及事件驱动模型,nodejs的性能非常高,而且想达到多核或者多进程也不是很难(现在已经有大量的第三方module来实现这个功能)。这里介绍一下事件驱动编程。 以下两...
  • 经典软件设计模型 - 事件驱动模型

    万次阅读 2013-06-27 15:06:38
    模型说明 在UI编程中,常常要对鼠标点击进行相应,首先如何获得鼠标点击呢? 方式一:创建一个线程,该线程一直循环检测是否有鼠标点击,那么这个方式有以下几个缺点: 1. CPU资源浪费,可能鼠标点击的频率非常小,...
  • Spring基于事件驱动模型的订阅发布模式代码实例详解,http://www.zuidaima.com/share/1791499571923968.htm
  • 基于嵌入式LINUX的异步事件驱动数据采集模型.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 385,442
精华内容 154,176
关键字:

事件驱动模型