Spring事件執行流程原始碼分析
1. 背景
為啥突然想到寫這個?起因就是看到了Nacos的#3757 ISSUE
,理解錯誤, 以為是服務啟動,沒有註冊上服務,實際namespace不同,導致服務無法註冊。 但這絲毫不影響再去研究了一波程式碼,順便也看到了Nacos是如何利用Spring的事件來進行服務註冊的。分享一波,歡迎大家學習指正!
2. 娓娓道來 - Spring事件
2.1 怎麼釋出事件
我們大家應該都知道,在Spring中,是通過實現org.springframework.context.ApplicationEventPublisher
來發佈一個事件。ApplicationEcentPublisher
是一個介面,我們來看一下介面中邏輯。
public interface ApplicationEventPublisher { /** * Notify all <strong>matching</strong> listeners registered with this * application of an application event. Events may be framework events * (such as RequestHandledEvent) or application-specific events. * @param event the event to publish * @see org.springframework.web.context.support.RequestHandledEvent */ default void publishEvent(ApplicationEvent event) { publishEvent((Object) event); } /** * Notify all <strong>matching</strong> listeners registered with this * application of an event. * <p>If the specified {@code event} is not an {@link ApplicationEvent}, * it is wrapped in a {@link PayloadApplicationEvent}. * @param event the event to publish * @since 4.2 * @see PayloadApplicationEvent */ void publishEvent(Object event); }
介面很簡單,裡面有兩個方法,都是釋出事件,而介面預設實現,是呼叫publishEvent(Object event)
的實現,唯一的區別就是default方法的引數是具體的事件類。
既然這是個介面,那一定有實現類,那麼我們肯定是要進入實現類,快捷鍵,檢視一下具體實現類。
我們通過實現類,可以看到,應該是從AbstractApplicationContext
進去(其他Context都是實現類,憑感覺和程式碼提示,從第一個類進如程式碼)。
進去org.springframework.context.support.AbstractApplicationContext
,我們直奔主題,檢視是如何實現PublishEvent。具體程式碼如下:
/**
* Publish the given event to all listeners.
* @param event the event to publish (may be an {@link ApplicationEvent}
* or a payload object to be turned into a {@link PayloadApplicationEvent})
* @param eventType the resolved event type, if known
* @since 4.2
*/
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 = (ApplicationEvent) event;
}
else {
// 將事件裝飾成PayloadApplicationEvent,
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) {
// 廣播還未初始化完成,放入earlyApplicationEvents.
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 核心關注點: 使用廣播廣播事件,
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);
}
}
}
在這段程式碼中,我們得到了兩點有用的資訊,分別是:
- 釋出事件的整體流程。(封裝事件,廣播事件)
- 釋出的時間通過
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
廣播事件
通過程式碼可以知道,我們接下來的關注點是getApplicationEventMulticaster()
,如何通過multicastEvent()
方法將事件廣播出去呢?
2.2 什麼是ApplicationEventMulticaster
進入到核心關注點程式碼,getApplicationEventMulticaster()
是什麼?
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
// ...略... 沒什麼重要資訊
return this.applicationEventMulticaster;
}
程式碼很簡單,沒什麼邏輯,但是讓我們明白了getApplicationEventMulticaster()
獲取到的物件是ApplicationEventMulticaster
,那我們接下來搞懂這個東西(ApplicationEventMulticaster
)是如何初始化?理清楚後可以弄清楚邏輯了。
輕輕鬆鬆翻到ApplicationEventMulticaster
初始化的程式碼(給自己鼓個掌