【daisy-framework】事件监听机制(event,listener)

前言

Github:https://github.com/yihonglei/daisy-framework

一 事件监听

Java很多web框架源码都能看到很多事件监听的存在,主要解决的问题就是类似观察者模式的行为,

当一件事情发生的时候,其他的相关事情需要知道并处理。

事件监听三要素: 事件源,事件对象,事件监听器。

1、监听器需要注册,即注册到某个数据结构,map或list等等存储起来;

2、一件事情的发生源头,这件事情需要触发监听器,让监听器能感受到,监听器才能去处理业务;

3、调用每一个监听器,执行相关处理;

要理解监听器,需要理解几个问题,监听器存储在什么数据结构?监听器如何被触发?监听器如何处理业务?

上一个代码吧,要不然太蒙了!

二 实例代码

demo基于spring boot环境。

1、自定义事件

Java自定义事件实现EventObject对象。

package com.jpeony.common.event;

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.EventObject;

/**
 * 自定义事件
 *
 * @author yihonglei
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class DaisyEvent extends EventObject {
    DaisyEventEnum event;
    Object[] args;

    /**
     * 事件对象
     *
     * @param daisyEventEnum 事件类型
     * @param source         触发事件初始化对象
     * @param args           其他参数
     * @author yihonglei
     */
    public DaisyEvent(DaisyEventEnum daisyEventEnum, Object source, Object... args) {
        super(source);
        this.event = daisyEventEnum;
        this.args = args;
    }

    /**
     * 事件对象
     *
     * @param daisyEventEnum 事件
     * @param source         触发事件初始化对象
     * @author yihonglei
     */
    public DaisyEvent(DaisyEventEnum daisyEventEnum, Object source) {
        this(daisyEventEnum, source, null);
    }
}

DaisyEventEnum事件类型,args事件参数。 

2、定义事件类型

定义事件类型主要是对可以针对事件类型进行发布。

package com.jpeony.common.event;

/**
 * 事件枚举
 *
 * @author yihonglei
 */
public enum DaisyEventEnum {
    /**
     * 初始化
     */
    INIT_EVENT,
    /**
     * 测试事件
     */
    TEST_EVENT;
}

3、事件监听器

事件监听器继承EventListener,定义事件发布的标准方法。

package com.jpeony.common.event;

import java.util.EventListener;

/**
 * 自定义事件监听,Spring的ApplicationListener也是这么干的,自定义方便自己对事件进行管理。
 * 注意,这里的事件是同步的,如果有需要,可以将事件提交到线程池异步化执行或者使用Spring的异步事件方式,
 * 它也是提交到线程池实现异步化。
 *
 * @author yihonglei
 */
public interface DaisyEventListener extends EventListener {
    /**
     * 触发事件
     *
     * @param e 相关事件
     */
    void fire(DaisyEvent e);
}

event1

package com.jpeony.core.service.event;

import com.jpeony.common.event.DaisyEvent;
import com.jpeony.common.event.DaisyEventEnum;
import com.jpeony.common.event.DaisyEventListener;
import com.jpeony.common.event.DaisyEventManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 实现daisyEventListener接口,在构造方法添加你的事件即可!
 *
 * @author yihonglei
 */
@Slf4j
@Component
public class Test1EventListener implements DaisyEventListener {

    public Test1EventListener() {
        DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);
    }

    @Override
    public void fire(DaisyEvent e) {
        try {
            String userName = (String) e.getSource();
            log.info("【Test1】对发布事件,事件触发,事件参数测试!userName={}", userName);
        } catch (Exception ex) {
            log.error("出异常了!", ex);
        }
    }
}

event2

package com.jpeony.core.service.event;

import com.jpeony.common.event.DaisyEvent;
import com.jpeony.common.event.DaisyEventEnum;
import com.jpeony.common.event.DaisyEventListener;
import com.jpeony.common.event.DaisyEventManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 实现daisyEventListener接口,在构造方法添加你的事件即可!
 *
 * @author yihonglei
 */
@Slf4j
@Component
public class Test2EventListener implements DaisyEventListener {

    public Test2EventListener() {
        DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);
    }

    @Override
    public void fire(DaisyEvent e) {
        try {
            String userName = (String) e.getSource();
            log.info("【Test2】对发布事件,事件触发,事件参数测试!userName={}", userName);
        } catch (Exception ex) {
            log.error("出异常了!", ex);
        }
    }
}

spring boot启动时注册到时间管理器DaisyEventManager(下面有源码)。

4、事件管理器

负责事件的注册,事件触发。

package com.jpeony.common.event;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.EnumMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 事件管理器
 *
 * @author yihonglei
 */
public class DaisyEventManager {
    private static Logger logger = LoggerFactory.getLogger(DaisyEventManager.class);
    private static EnumMap<DaisyEventEnum, CopyOnWriteArrayList<DaisyEventListener>> eventListeners = new EnumMap<>(DaisyEventEnum.class);

    public static void addListener(DaisyEventEnum event, DaisyEventListener el) {
        CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(event);
        if (listeners == null) {
            listeners = new CopyOnWriteArrayList<>();
            eventListeners.put(event, listeners);
        } else {
            for (DaisyEventListener listener : listeners) {
                if (listener.getClass() == el.getClass()) {
                    logger.info("more than one instances added, this class is {}", listener.getClass());
                    return;
                }
            }
        }
        listeners.add(el);
    }

    /**
     * 移出指定事件的指定监听器
     *
     * @author yihonglei
     */
    public static synchronized void removeListener(DaisyEventEnum event, DaisyEventListener el) {
        CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(event);
        if (listeners != null) {
            listeners.remove(el);
        }
    }

    /**
     * 清除指定事件的所有监听器
     *
     * @author yihonglei
     */
    public static void purgeListeners(DaisyEventEnum event) {
        CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(event);
        if (listeners != null) {
            listeners.clear();
            // 移除该事件
            eventListeners.remove(event);
        }
    }

    /**
     * 触发事件
     *
     * @author yihonglei
     */
    public static void fireEvent(DaisyEvent obj) {
        CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(obj.getEvent());
        if (listeners == null) {
            return;
        }
        for (DaisyEventListener listener : listeners) {
            listener.fire(obj);
        }
    }
}

spring boot启动的时候,Test1EventListener和Test2EventListener通过构造器的事件管理器注册到EnumMap。

DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);

EnumMap是一个事件类型对应多个listener的数据结构,Map的key是事件类型,value是监听的list集合。

5、测试

package com.jpeony.test.service.event;

import com.jpeony.common.event.DaisyEvent;
import com.jpeony.common.event.DaisyEventEnum;
import com.jpeony.common.event.DaisyEventManager;
import com.jpeony.test.BaseServletTest;
import org.junit.Test;

/**
 * @author yihonglei
 */
public class EventTest extends BaseServletTest {

    @Test
    public void test() {
        // 事件发布,支持任意参数,可以是一个值,也可以传入对象
        String userName = "Tom";
        DaisyEventManager.fireEvent(new DaisyEvent(DaisyEventEnum.TEST_EVENT, userName));
    }

}

发布TEST_EVENT事件,event1和event2监听并执行。

DaisyEventManager.fireEvent(new DaisyEvent(DaisyEventEnum.TEST_EVENT, userName));

传入TEST_EVENT,事件管理器根据key为TEST_EVENT取出对应的监听器列表并循环执行。

 

6、总结

1)监听事件继承EventObject;

2)监听器继承于EventLisneter,也可以不继承,这个接口只是一个标记接口,然后定义统一的事件发布方法;

3)弄一个事件管理器,不弄也行,我这里是为了方便,你可以在某个类里面搞一个Map或list存这些监听器,

数据结构选取可以根据自己的业务来,总之,要把这些监听器对象存起来;

4)事件执行就比较简单,就是根据事件类型取出相应的监听器,循环执行,完事!

三 Tomcat的事件监听

最近无聊看了下tomcat源码,单独简单聊下tomcat里的事件监听怎么实现的。

1、事件定义

你会发现,tomcat生命周期管理的自定义事件也只是继承了EventObject,data事件监听器处理需要的参数,

type就是事件类型。

2、监听器定义

刚才我自己实现监听器,标识接口用的Java的EventListener,这个是只是个标记接口,

表明跟我这个EventListener相关的都是要实现监听器,你别的不相关的别瞎搞,只是一个规范而已。

tomcat里面就只是自己定义了事件发布标准,监听器都走LifecycleListener接口标识。

3、监听注册

数据结构

最后都是调用add方法,把监听都放在这个list里面。

4、监听触发

 

这个地方也是通过type取到监听list,然后循环触发执行。

四 事件监听本质思想

1、事件自定义,继承EventObject;

2、监听器定义,要统一标识接口和发布方法;

3、监听器注册,搞一个合适的数据结构把你的监听分类存起来;

4、事件触发,根据事件类型,从数据结构取出相应监听器,循环执行;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章