一、事件驅(qū)動模型簡介
事件驅(qū)動模型,也即是我們通常說的觀察者。基于發(fā)布-訂閱模式的編程模型。
概念
定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生變化時,所有依賴它的對象都得到通知并自動更新。
百度百科:
從事件角度說,事件驅(qū)動程序的基本結(jié)構(gòu)是由一個事件收集器、一個事件發(fā)送器和一個事件處理器組成。
事件收集器專門負(fù)責(zé)收集所有事件,包括來自用戶的(如鼠標(biāo)、鍵盤事件等)、來自硬件的(如時鐘事件等)和來自軟件的(如操作系統(tǒng)、應(yīng)用程序本身等)。
事件發(fā)送器負(fù)責(zé)將收集器收集到的事件分發(fā)到目標(biāo)對象中。
事件處理器做具體的事件響應(yīng)工作。
從程序設(shè)計的角度來看,事件驅(qū)動模型的核心構(gòu)件通常包含以下幾個:
- 事件源(Event Source):負(fù)責(zé)產(chǎn)生事件的對象。比如我們常見的按鈕,按鈕就是一個事件源,能夠產(chǎn)生“點擊”這個事件
- 事件監(jiān)聽器(Event Listener):注冊在事件源上才能被調(diào)用,主要用于監(jiān)聽事件并進(jìn)行事件處理或者轉(zhuǎn)發(fā)。
- 事件對象(Event Object):或者稱為事件對象,是事件源和事件監(jiān)聽器之間的信息橋梁。是整個事件模型驅(qū)動的核心
下圖展示了事件、事件源、監(jiān)聽器直接的關(guān)系:
1、 觀察者模式
原理解析
觀察者模式的UML圖如下:
具體類說明如下:
- Observer觀察者:即為事件模型中的事件監(jiān)聽器,該接口定義一個方法update,即為事件處理方法。當(dāng)觀察到有事件產(chǎn)生時,該方法便會處理
- Subject被觀察者:即事件模型中的事件源,負(fù)責(zé)產(chǎn)生事件,定義與觀察者建立關(guān)聯(lián)的方法(添加觀察者、刪除觀察者、通知觀察者)
- ConreteObserver具體的觀察者實現(xiàn)類:實現(xiàn)Observer接口中的update方法,具體實例會被添加到被觀察者的觀察者隊列中(observers[List])
- ConreteSubject具體的被觀察者實現(xiàn)類:實現(xiàn)Subject接口。定義觀察者隊列(observers[List]),并定義實現(xiàn)如何將觀察者對象添加到觀察者隊列中以及如何通所有知觀察者
在上述類圖中,具體的ConreteSubject被觀察者,其中包含observers一個列表,保存所有觀察者對象。doAction方法是需要通知觀察者對象的動作,當(dāng)該方法執(zhí)行后,會通知保存在observers中的所有觀察者。
也就是在執(zhí)行方法ConreteSubject#doAction()時,需要調(diào)用ConreteSubject#notifyObservers()通知保存在observers中的所有觀察者,讓其能夠做出響應(yīng)。
Spring 中的事件監(jiān)聽機制
Spring 中的事件
Spring 中的事件通知機制就是觀察者模式的一種實現(xiàn)。觀察者是 ApplicationListener,可以實現(xiàn)接口定義觀察者,也可以使用注解定義觀察者。觀察者感興趣的是某種狀態(tài)的變化,這種狀態(tài)變化使用 ApplicationEvent 來傳達(dá),也就是事件對象。我們說的 Spring 中的事件,就是 ApplicationEvent。在事件中,被觀察者可以認(rèn)為是發(fā)出事件的一方,只有在狀態(tài)變化時才發(fā)布事件。
當(dāng)有狀態(tài)發(fā)生變化時,發(fā)布者調(diào)用 ApplicationEventPublisher 的 publishEvent 方法發(fā)布一個事件,Spring 容器廣播事件給所有觀察者,調(diào)用觀察者的 onApplicationEvent 方法把事件對象傳遞給觀察者。調(diào)用 publishEvent 方法有兩種途徑,一種是實現(xiàn)接口由容器注入 ApplicationEventPublisher 對象然后調(diào)用其方法,另一種是直接調(diào)用容器的方法,兩種方法發(fā)布事件沒有太大區(qū)別。
相關(guān)類型總結(jié)如下:
- ApplicationListener:事件監(jiān)聽者,觀察者;
- ApplicationEvent:Spring 事件,記錄事件源、時間和數(shù)據(jù);
- ApplicationEventPublisher:發(fā)布事件;
優(yōu)勢
使用事件可以解耦代碼,觀察者與被觀察者可以分開開發(fā),中間只有事件作為聯(lián)系,不用關(guān)心另一方如何實現(xiàn)。觀察者可以有多個,所以對于同一個事件可以有多種不同的處理方式,不過要確保不依賴處理的順序。使用事件后,觀察者可以單獨開發(fā),對主流程沒有任何影響,可以簡化主流程的開發(fā)。
事件可以用于各種場景的消息通知,比如系統(tǒng)收到停機指令后,可以發(fā)出停機事件,這樣相關(guān)的子系統(tǒng)就可以進(jìn)行停機前的各種處理。那么 Spring 中的事件是如何實現(xiàn)的呢?
Spring事件驅(qū)動模型的三大組成部分
ApplicationEvent:事件對象,Spring事件驅(qū)動模型中的對象源,繼承JDK EventObject,通過在發(fā)布事件時通過EventObject.source字段攜帶事件相關(guān)的數(shù)據(jù)。
ApplicationListener:應(yīng)用監(jiān)聽器,負(fù)責(zé)監(jiān)聽事件對象是否有發(fā)生變化,實現(xiàn)該接口并實現(xiàn)onApplicationEvent方法,完成事件發(fā)生變化時的邏輯處理
ApplicationEventPublisher:事件發(fā)布器,定義了事件發(fā)布規(guī)范,只定義了接口,具體的實現(xiàn)交由其他類中實現(xiàn)。Spring提供了SimpleApplicationEventMulticaster實現(xiàn)了廣播事件發(fā)布器。
Spring 框架提供了四種容器事件,我們可以直接觀察,包括:
- ContextStartedEvent:ApplicationContext 啟動事件。
- ContextRefreshedEvent:ApplicationContext 更新事件。
- ContextStoppedEvent:ApplicationContext 停止事件。
- ContextClosedEvent:ApplicationContext 關(guān)閉事件。
Spring 4.2 之前的版本,事件必須繼承 ApplicationEvent,從 Spring 4.2 版開始,框架提供了 PayloadApplicationEvent 用于包裝任意類型,不強制事件對象繼承 ApplicationEvent。當(dāng)我們發(fā)送一個任意類型的事件對象時,框架內(nèi)部自動包裝為 PayloadApplicationEvent 事件對象。
事件監(jiān)聽者
事件監(jiān)聽者 ApplicationListener 繼承自 JDK 的 EventListener ,JDK 要求所有監(jiān)聽者繼承它。監(jiān)聽者只有一個方法 onApplicationEvent,用來處理事件 ApplicationEvent。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
在容器啟動的時候檢測應(yīng)用中的監(jiān)聽者并把用戶實現(xiàn)的監(jiān)聽者注冊到 SimpleApplicationEventMulticaster 集合中。
Spring 也支持直接注解的形式進(jìn)行事件監(jiān)聽,使用注解 @EventListener 即可。使用注解時,方法可以返回任意類型,如果返回值不為 void 則當(dāng)做一個新事件再次發(fā)布。
@Service
public class AnnoEventListener {
@EventListener
public void listen(MyEvent myEvent){
System.out.println("receive " + myEvent.getSource());
}
}
注解形式的監(jiān)聽者是通過 EventListenerMethodProcessor 注冊到容器中的。該類定義了一個 Bean,在初始化完成后,調(diào)用它的后置回調(diào)方法 afterSingletonsInstantiated,在方法中遍歷容器中所有的 bean,提取出 EventListener 注解修飾的方法并根據(jù)注解的參數(shù)創(chuàng)建 ApplicationListener 對象加入到 ApplicationContext 監(jiān)聽列表中。
發(fā)布事件
ApplicationEventPublisher 接口定義了事件發(fā)布方法,代碼如下:
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
ApplicationContext 接口繼承了 ApplicationEventPublisher ,并在 AbstractApplicationContext 實現(xiàn)了具體代碼,實際執(zhí)行是委托給 ApplicationEventMulticaster。
ApplicationEventMulticaster 接口定義了對監(jiān)聽者的操作,如增加監(jiān)聽者、移除監(jiān)聽者,以及發(fā)布事件的方法。框架提供了 ApplicationEventMulticaster 接口的默認(rèn)實現(xiàn)SimpleApplicationEventMulticaster,如果本地容器中沒有 ApplicationEventMulticaster 的實現(xiàn)就會使用這個默認(rèn)實現(xiàn)。
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void removeApplicationListener(ApplicationListener<?> listener);
void removeApplicationListenerBean(String listenerBeanName);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
在 SimpleApplicationEventMulticaster 中,可以看到 multicastEvent 方法中遍歷了所有的 Listener,并依次調(diào)用 Listener 的 onApplicationEvent 方法發(fā)布事件。Spring 還提供了異步發(fā)布事件的能力,taskExecutor 不為 null 時即異步執(zhí)行。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Nullable
private Executor taskExecutor;
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
}
注意事項:
可以看到,Spring 處理事件是同步的,監(jiān)聽者的執(zhí)行時間最好不要太長,以免影響主流程。如果事件的處理一定要耗費比較長的時間,請使用異步方式進(jìn)行處理。
高級監(jiān)聽者
SmartApplicationListener
SmartApplicationListener 接口是 ApplicationListener 的子接口,還繼承了 Ordered 接口。SmartApplicationListener 定義了兩個 support 方法用于判斷事件類型、來源類型是否和當(dāng)前監(jiān)聽者匹配,這樣監(jiān)聽者可以篩選自己感興趣的事件和來源。繼承 Ordered 接口后,該監(jiān)聽者具備了排序的功能,可以按照 order 從小到大的順序給監(jiān)聽者確定一個優(yōu)先級,從而確保執(zhí)行順序。
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
int getOrder();
}
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
//指定支持哪些類型的事件
//判斷事件的類型是否和當(dāng)前監(jiān)聽者匹配
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
//指定支持發(fā)生事件所在的類型
//判斷事件源類型是否和當(dāng)前監(jiān)聽者匹配
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}
GenericApplicationListener
GenericApplicationListener 接口是 ApplicationListener 的子接口,也繼承了 Ordered 接口,同 SmartApplicationListener 一樣具有事件篩選能力和排序能力。但篩選事件使用的是 ResolvableType 類型,而不是 ApplicationEvent 類型。
ResolvableType類型是Spring4之后添加進(jìn)來的獲取泛型信息的工具,通過ResolvableType可以獲取到傳入的泛型的各種信息。
public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
//指定支持哪些類型的事件
//判斷事件的類型是否和當(dāng)前監(jiān)聽者匹配
boolean supportsEventType(ResolvableType eventType);
//指定支持發(fā)生事件所在的類型
//判斷事件源類型是否和當(dāng)前監(jiān)聽者匹配
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}
參考:
https://blog.csdn.net/zrudong/article/details/78567473