【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、事件觸發,根據事件類型,從數據結構取出相應監聽器,循環執行;

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