微信公衆號:IT學習筆記
學習更多源碼知識,歡迎關注。java
注意:spring源碼分析文章對應spring版本爲 5.1.xgit
目錄github
1,概述spring
2.1 事件設計模式
2.2 事件監聽器bash
3,結語app
要想理解spring的事件機制,我以爲首先本身動手去擼一套簡單的自定義事件驅動編程demo仍是很是有必要滴,由於這樣有助於理解spring事件機制。固然,這裏也是模仿spring的事件機制的代碼,不過下面看代碼實現時能夠先拋開spring的事件機制相關代碼,將注意力集中到這個簡單demo上便可。
在看這個自定義事件驅動編程時,首先要熟悉觀察者設計模式,由於事件驅動編程能夠說是觀察者(發佈訂閱)模式的具體實現。推薦個人另外一篇翻譯的博文:觀察者模式--設計模式(一),有須要的小夥伴們能夠先學習下哈。
下面正式開始手擼代碼實現,首先先放上下面代碼的github地址:
由於這篇文章是spring事件機制的前置文章,所以這裏自定義實現一個模擬容器(能夠理解爲spring容器,servltet容器等)的生命週期事件的簡單demo。
先來看一下事件的總體架構圖,讓你們先有對事件有一個總體的認識,以下圖:
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 {
}複製代碼
先來看一下事件監聽器的總體架構圖,讓你們先有對事件監聽器有一個總體的認識,以下圖:
其中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());
}
}
}複製代碼
先看下類圖:
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;
}
}
複製代碼
那麼直接上測試代碼,下面只演示同步發佈事件的功能:
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(),運行結果以下截圖:
好了,自定義事件驅動編程的簡單demo就已經實現了。
這只是spring事件機制源碼分析的前置文章,真正的源碼分析請見下一篇博文: