1. 事件監(jiān)聽器模式的重要因素
-
Event Object: 事件,事件源會將事件進(jìn)行發(fā)布恶阴。Spring中的事件對象為
ApplicationEvent
. -
Event Listener: 事件監(jiān)聽器刹勃,負(fù)責(zé)處理訂閱的事件. Spring中對應(yīng)的事件監(jiān)聽器接口為
ApplicationListener
. -
Event Source: 事件源钠导,負(fù)責(zé)發(fā)布事件并通知事件監(jiān)聽器芜抒。Spring中對應(yīng)的事件源接口為
ApplicationEventPublisher
.
關(guān)于事件監(jiān)聽器模式锅纺,如果你不夠熟悉,可以在我的上篇博客得到解答->點(diǎn)我前往
2. ApplicationEvent
2.1 接口清單
值得一提的是铣耘,Spring中的ApplicationEvent是內(nèi)置事件源的洽沟,這意味著在監(jiān)聽器中可以獲取事件源,而Spring中的事件源通常為容器本身.
同時蜗细,事件發(fā)生的同時裆操,會記錄當(dāng)前系統(tǒng)時間戳
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context;
import java.util.EventObject;
/**
* Class to be extended by all application events. Abstract as it
* doesn't make sense for generic events to be published directly.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened. */
private final long timestamp;
/**
* Create a new ApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null})<br>
* 創(chuàng)建一個應(yīng)用事件,其中source為事件源炉媒,并且不能為空.
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Return the system time in milliseconds when the event happened.<br>
* 返回當(dāng)前事件發(fā)生時的系統(tǒng)時間
*/
public final long getTimestamp() {
return this.timestamp;
}
}
2.2 UML
看類圖可以知道踪区,ApplicationEvent繼承自JDK的EventObject,同時還有一個抽象子類
ApplicationContextEvent
,為什么要再抽象出這個子類呢吊骤,因?yàn)镺bject本身的含義太過廣泛缎岗,Spring為了定義容器事件,強(qiáng)制約束source對象本身的類型為ApplicationContext.
最后從這個抽象類白粉,衍生了一系列單一職責(zé)的事件传泊。分別對應(yīng)容器的關(guān)閉、刷新蜗元、啟動或渤、停止等階段.
2.3 PayloadApplicationEvent
Spring 4.2 后推出一個一個基于泛型對事件進(jìn)行包裝的類,在此之前奕扣,發(fā)布的Event都必須繼承自ApplicationEvent.加入Payload機(jī)制后薪鹦,在發(fā)布事件的時候,可以傳輸任意的Object,Spring內(nèi)部都會用PayloadApplicationEvent
對事件進(jìn)行包裝.
具體的改進(jìn)在org.springframework.context.ApplicationEventPublisher#publishEvent(java.lang.Object)
可以做更加深入的了解惯豆。
3. ApplicationListener
3.1 接口清單
函數(shù)式接口池磁,同時監(jiān)聽的事件需為繼承自ApplicationEvent的類,如果Spring為4.2后的楷兽,可以不受這個限制地熄,因?yàn)閮?nèi)部使用
PayloadApplicationEvent
進(jìn)行了包裝,在事件源發(fā)布事件時芯杀,會觸發(fā)onApplicationEvent
通知監(jiān)聽器端考。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
* 處理應(yīng)用事件
*/
void onApplicationEvent(E event);
}
3.2 UML
-
SmartApplicationListener: 支持事件消費(fèi)排序與事件類型匹配,Spring3.0開始支持揭厚,需要實(shí)現(xiàn)的方法為
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
,注意這里推斷的對象是Class. -
GenericApplicationListener: 支持事件消費(fèi)排序與事件類型匹配却特,Spring4.2開始支持,需要實(shí)現(xiàn)的方法為
boolean supportsEventType(ResolvableType eventType);
,注意這里推斷的對象是ResolvableType
.
3.3 注解支持-@EventListener
Spring4.2后筛圆,對事件監(jiān)聽器增加了注解的支持裂明,無需實(shí)現(xiàn)接口,只需要通過@EventListener
來直接在需要響應(yīng)的方法上標(biāo)注即可太援,配合Async
和@Order
注解還可以支持異步與消費(fèi)順序聲明.
注意闽晦,注解標(biāo)注的方法上扳碍,最好聲明為void,如果返回類型為數(shù)組或者集合,Spring會將每個元素作為新的事件進(jìn)行發(fā)布仙蛉。
4.ApplicationEventPublisher&ApplicationEventMulticaster
4.1 ApplicationEventPublisher
4.1.1 接口清單
通常在SpringIOC中笋敞,容器本身會作為ApplicationEventPublisher去進(jìn)行事件的發(fā)布.
同時,開發(fā)者還可以通過Aware接口訪問到ApplicationEventPublisher實(shí)例.來發(fā)布自定義事件.
@FunctionalInterface
public interface ApplicationEventPublisher {
// 通知所有與此應(yīng)用程序注冊的匹配偵聽器一個應(yīng)用程序事件捅儒。
// 事件可以是框架事件(例如ContextRefreshedEvent)或特定于應(yīng)用程序的事件液样。
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
// 通知所有與此應(yīng)用程序注冊的匹配偵聽器事件。
// 如果指定的事件不是ApplicationEvent巧还,則將其包裝在PayloadApplicationEvent中。
void publishEvent(Object event);
}
4.1.2 UML
這里再次驗(yàn)證坊秸,容器即為IOC的ApplicationEventPublisher.
4.2 ApplicationEventMulticaster
Spring中的publisher只提供了發(fā)布事件的接口麸祷,然而一個事件監(jiān)聽器模式少不了注冊監(jiān)聽器這件事情,ApplicationEventMulticaster
就是為了解決這件事而產(chǎn)生的褒搔。
4.2.1 接口清單
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);
}
從接口清單我們可以看到,ApplicationEventMulticaster支持添加監(jiān)聽器阶牍、移除監(jiān)聽器、發(fā)布事件星瘾。
4.2.2 UML
-
AbstractApplicationEventMulticaster: 內(nèi)部持有一個
defaultRetriever
成員變量走孽,該變量為ListenerRetriever
,其內(nèi)部使用Set存儲ApplicationListener
琳状。AbstractApplicationEventMulticaster
添加監(jiān)聽器的操作為this.defaultRetriever.applicationListeners.add(listener);
.有興趣的讀者可以自行擴(kuò)展閱讀. -
SimpleApplicationEventMulticaster: 繼承自
AbstractApplicationEventMulticaster
磕瓷,內(nèi)置private Executor taskExecutor;
-任務(wù)執(zhí)行器。默認(rèn)情況下念逞,監(jiān)聽器都以同步的方式進(jìn)行困食,但是會由于個別監(jiān)聽器速度過慢,導(dǎo)致任務(wù)進(jìn)度阻塞翎承,因此該事件發(fā)布器也支持了以線程池來提交異步任務(wù)的方式消費(fèi)事件.
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)) {
// 如果線程池不為空,則使用線程池提交任務(wù).
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
4.3 為什么要同時定義ApplicationEventMulticaster和ApplicationEventPublisher?
在org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
中硕盹,我們可以看到,容器內(nèi)部是怎么發(fā)送事件的.
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
// 如果事件對象不是ApplicationEvent叨咖,則使用PayloadApplicationEvent進(jìn)行包裝
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 重點(diǎn)瘩例,可以看到publishEvent其實(shí)是通過ApplicationEventMulticaster進(jìn)行真正的事件發(fā)布
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
// 一些遞歸的操作
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
簡單地說,就是ApplicationEventPublisher是一個簡單的事件發(fā)布接口甸各,只負(fù)責(zé)聲明入口垛贤,而ApplicationEventMulticaster負(fù)責(zé)處理真正的事件發(fā)布邏輯。這其實(shí)是一種委托的思想.你可以在Spring隨處發(fā)現(xiàn)這種現(xiàn)象痴晦,如Registry接口其實(shí)真正的執(zhí)行者為DefaultListableBeanFactory.
總結(jié)
- Spring中的事件監(jiān)聽器模式擴(kuò)展自JDK中的事件監(jiān)聽器模型南吮。
- 事件在早期版本必須繼承自
ApplicationEvent
,4.2后Spring引入了PayloadApplicationEvent
進(jìn)行兼容,支持以O(shè)bject的形式發(fā)送事件. - 事件源在IOC中通常為容器本身.
- 事件監(jiān)聽器支持實(shí)現(xiàn)接口和注解形式進(jìn)行實(shí)現(xiàn),同時可以使用
@Order
注解來指定消費(fèi)順序誊酌。 - 避免在事件監(jiān)聽器中調(diào)用事件源的發(fā)布事件部凑,引起循環(huán)引用的問題露乏。
- Spring以委托的思想,建立了事件發(fā)布者的接口視圖->
ApplicationEventPublisher
,其中真正的執(zhí)行者則為ApplicationEventMulticaster
接口的實(shí)現(xiàn)類. - ApplicationEventMulticaster提供了異步的支持涂邀。