模仿Spring事件機制實現自定義事件驅動編程--Spring的事件機制源碼分析(一)

微信公衆號:IT學習筆記
學習更多源碼知識,歡迎關注。java

注意:spring源碼分析文章對應spring版本爲 5.1.xgit


目錄github

1,概述spring

2,自定義事件驅動編程編程

2.1 事件設計模式

2.2 事件監聽器bash

2.3 事件發佈器微信

2.4 測試自定義的容器生命週期事件架構

3,結語app


1,概述

要想理解spring的事件機制,我以爲首先本身動手去擼一套簡單的自定義事件驅動編程demo仍是很是有必要滴,由於這樣有助於理解spring事件機制。固然,這裏也是模仿spring的事件機制的代碼,不過下面看代碼實現時能夠先拋開spring的事件機制相關代碼,將注意力集中到這個簡單demo上便可。

在看這個自定義事件驅動編程時,首先要熟悉觀察者設計模式,由於事件驅動編程能夠說是觀察者(發佈訂閱)模式的具體實現。推薦個人另外一篇翻譯的博文:觀察者模式--設計模式(一),有須要的小夥伴們能夠先學習下哈。

下面正式開始手擼代碼實現,首先先放上下面代碼的github地址:

github.com/jinyue233/j…

2,自定義事件驅動編程

由於這篇文章是spring事件機制的前置文章,所以這裏自定義實現一個模擬容器(能夠理解爲spring容器,servltet容器等)的生命週期事件的簡單demo。

2.1 事件

先來看一下事件的總體架構圖,讓你們先有對事件有一個總體的認識,以下圖:


1,Event接口

面向接口編程,首先先定義一個Event接口,該接口沒有任何方法,能夠說是事件的標誌接口。

public interface Event extends Serializable {
}複製代碼

2,AbstractContextEvent

AbstractContextEvent是容器事件的基本抽象類,由於事件也能夠攜帶數據,所以這裏定義了一個timestamp屬性,用來記錄事件的發生時間。

public class AbstractContextEvent implements Event {
    private static final long serialVersionUID = -6159391039546783871L;

    private final long timestamp = System.currentTimeMillis();

    public final long getTimestamp() {
        return this.timestamp;
    }
}複製代碼

3,ContextStartEvent

ContextStartEvent事件是AbstractContextEvent具體實現類,容器開始啓動時觸發,這裏爲了demo簡單,這裏再也不定義任何事件邏輯,只是表明容器啓動時的一個標誌事件類。

public class ContextStartEvent extends AbstractContextEvent {
}複製代碼

4,ContextRunningEvent

ContextRunningEvent事件是AbstractContextEvent具體實現類,容器啓動後時觸發,這裏爲了demo簡單,這裏再也不定義任何事件邏輯,只是表明容器啓動運行時時的一個標誌事件類。

public class ContextRunningEvent extends AbstractContextEvent {
}複製代碼

5,ContextDestroyEvent

ContextDestroyEvent事件是AbstractContextEvent具體實現類,容器銷燬時觸發,這裏爲了demo簡單,這裏再也不定義任何事件邏輯,只是表明容器銷燬時的一個標誌事件類。

public class ContextDestroyEvent extends AbstractContextEvent {
}複製代碼

2.2 事件監聽器

先來看一下事件監聽器的總體架構圖,讓你們先有對事件監聽器有一個總體的認識,以下圖:


其中EventListener是全部事件監聽器的基類接口,,是事件監聽器的標誌類接口,被全部具體的事件監聽器實現。而後ContextListener接口是容器事件監聽器接口,繼承了EventListener,主要定義了以下事件監聽方法:

void onApplicationEvent(T event);複製代碼

而後ContextListener接口被三個具體的容器生命週期事件監聽器實現,分別是ContextStartEventListener(監聽容器啓動時的ContextStartEvent),ContextRunningEventListener(監聽容器啓動後運行時的ContextRunningEvent)和ContextDestroyEventListener(監聽容器銷燬時的ContextDestroyEvent)。

下面看具體的代碼實現:

public interface EventListener {
}


public interface ContextListener<T extends AbstractContextEvent> extends EventListener {
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(T event);
}


public class ContextStartEventListener implements ContextListener<AbstractContextEvent> {
    /**
     * Handle an application event.
     *
     * @param event the event to respond to
     */
    public void onApplicationEvent(AbstractContextEvent event) {
        if (event instanceof ContextStartEvent) {
            System.out.println("容器啓動。。。,啓動時間爲:" + event.getTimestamp());
        }
    }
}

public class ContextRunningEventListener implements ContextListener<AbstractContextEvent> {
    /**
     * Handle an application event.
     *
     * @param event the event to respond to
     */
    public void onApplicationEvent(AbstractContextEvent event) {
        if (event instanceof ContextRunningEvent) {
            System.out.println("容器開始運行。。。");
            try {
                Thread.sleep(3000);
                System.out.println("容器運行結束。。。");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ContextDestroyEventListener implements ContextListener<AbstractContextEvent> {
    /**
     * Handle an application event.
     *
     * @param event the event to respond to
     */
    public void onApplicationEvent(AbstractContextEvent event) {
        if (event instanceof ContextDestroyEvent) {
            System.out.println("容器銷燬。。。,銷燬時間爲:" + event.getTimestamp());
        }
    }
}複製代碼

2.3 事件發佈器

先看下類圖:


ApplicationEventMulticaster是發佈事件的父類接口,主要定義了增長,刪除,獲取等操做事件監聽器的的方法接口,此外,還定義了一個發佈事件的方法。

SimpleApplicationEventMulticaster是ApplicationEventMulticaster事件發佈器接口的默認實現類,主要承擔發佈事件的功能。其內部維護了一個事件監聽器列表contextListeners,當發佈事件時會遍歷這些列表,而後再向每一個監聽器發佈事件,經過設置async屬性來決定同步廣播事件仍是異步廣播事件。

下面看看實現代碼:

public interface ApplicationEventMulticaster {
    void addContextListener(ContextListener<?> listener);

    void removeContextListener(ContextListener<?> listener);

    void removeAllListeners();

    void multicastEvent(AbstractContextEvent event);

}



public class SimpleApplicationEventMulticaster implements ApplicationEventMulticaster {
    // 是否異步發佈事件
    private boolean async = false;
    // 線程池
    private Executor taskExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    // 事件監聽器列表
    private List<ContextListener<?>> contextListeners = new ArrayList<ContextListener<?>>();

    
    public void addContextListener(ContextListener<?> listener) {
        contextListeners.add(listener);
    }

    public void removeContextListener(ContextListener<?> listener) {
        contextListeners.remove(listener);
    }

    public void removeAllListeners() {
        contextListeners.clear();
    }

    public void multicastEvent(AbstractContextEvent event) {
        doMulticastEvent(contextListeners, event);
    }

    private void doMulticastEvent(List<ContextListener<?>> contextListeners, AbstractContextEvent event) {
        for (ContextListener contextListener : contextListeners) {
            // 異步廣播事件
            if (async) {
                taskExecutor.execute(() -> invokeListener(contextListener, event));
                // new Thread(() -> invokeListener(contextListener, event)).start();
            // 同步發佈事件,阻塞的方式
            } else {
                invokeListener(contextListener, event);
            }
        }
    }

    private void invokeListener(ContextListener contextListener, AbstractContextEvent event) {
        contextListener.onApplicationEvent(event);
    }

    public void setAsync(boolean async) {
        this.async = async;
    }
}
複製代碼

2.4 測試自定義的容器生命週期事件

那麼直接上測試代碼,下面只演示同步發佈事件的功能:

public class MockSpringEventTest {

    @Test
    public void testContextLifecycleEventInSync() {
        // 新建SimpleApplicationEventMulticaster對象,並添加容器生命週期監聽器
        ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        eventMulticaster.addContextListener(new ContextStartEventListener());
        eventMulticaster.addContextListener(new ContextRunningEventListener());
        eventMulticaster.addContextListener(new ContextDestroyEventListener());
        // 發射容器啓動事件ContextStartEvent
        eventMulticaster.multicastEvent(new ContextStartEvent());
        // 發射容器正在運行事件ContextRunningEvent
        eventMulticaster.multicastEvent(new ContextRunningEvent());
        // 發射容器正在運行事件ContextDestroyEvent
        eventMulticaster.multicastEvent(new ContextDestroyEvent());
    }

    @Test
    public void testContextLifecycleEventInAsync() throws InterruptedException {
        // 新建SimpleApplicationEventMulticaster對象,並添加容器生命週期監聽器
        ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        eventMulticaster.addContextListener(new ContextStartEventListener());
        eventMulticaster.addContextListener(new ContextRunningEventListener());
        eventMulticaster.addContextListener(new ContextDestroyEventListener());

        ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true);

        // 發射容器啓動事件ContextStartEvent
        eventMulticaster.multicastEvent(new ContextStartEvent());
        // 發射容器正在運行事件ContextRunningEvent
        eventMulticaster.multicastEvent(new ContextRunningEvent());
        // 發射容器正在運行事件ContextDestroyEvent
        eventMulticaster.multicastEvent(new ContextDestroyEvent());
        // 這裏沒明白在沒有用CountDownLatch的狀況下爲什麼主線程退出,非後臺線程的子線程也會退出???爲了測試,全部先用CountDownLatch鎖住main線程先
        // 通過測試,原來是由於用了junit的方法,test方法線程退出後,test方法線程產生的非後臺線程也隨之退出,而下面的main方法啓動的非後臺線程則不會
        // TODO 這是爲何呢???難道是A子線程(非main線程)啓動的B子線程會隨着A子線程退出而退出?還沒驗證
        CountDownLatch countDownLatch = new CountDownLatch(1);
        countDownLatch.await();

    }

    public static void main(String[] args) throws InterruptedException {
        // 新建SimpleApplicationEventMulticaster對象,並添加容器生命週期監聽器
        ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        eventMulticaster.addContextListener(new ContextStartEventListener());
        eventMulticaster.addContextListener(new ContextRunningEventListener());
        eventMulticaster.addContextListener(new ContextDestroyEventListener());

        ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true);

        // 發射容器啓動事件ContextStartEvent
        eventMulticaster.multicastEvent(new ContextStartEvent());
        // 發射容器正在運行事件ContextRunningEvent
        eventMulticaster.multicastEvent(new ContextRunningEvent());
        // 發射容器正在運行事件ContextDestroyEvent
        eventMulticaster.multicastEvent(new ContextDestroyEvent());

    }
}
複製代碼

經過運行測試方法testContextLifecycleEventInSync(),運行結果以下截圖:


3,結語

好了,自定義事件驅動編程的簡單demo就已經實現了。

這只是spring事件機制源碼分析的前置文章,真正的源碼分析請見下一篇博文:

Spring事件相關類關係源碼解析--Spring的事件機制源碼分析(二

相關文章
相關標籤/搜索