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

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

 

目錄

1,概述

2,自定義事件驅動編程

2.1 事件

2.2 事件監聽器

2.3 事件發佈器

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

3,結語


 

1,概述

要想理解spring的事件機制,我覺得首先自己動手去擼一套簡單的自定義事件驅動編程demo還是非常有必要滴,因爲這樣有助於理解spring事件機制。當然,這裏也是模仿spring的事件機制的代碼,不過下面看代碼實現時可以先拋開spring的事件機制相關代碼,將注意力集中到這個簡單demo上即可。

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

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

https://github.com/jinyue233/java-demo/tree/master/spring5-demo/src/main/java/com/jinyue/spring/event/mockspringevent

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的事件機制源碼分析(二

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