1. 程式人生 > 實用技巧 >Spring事件執行流程原始碼分析

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);
			}
		}
	}

在這段程式碼中,我們得到了兩點有用的資訊,分別是:

  1. 釋出事件的整體流程。(封裝事件,廣播事件)
  2. 釋出的時間通過getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);廣播事件

通過程式碼可以知道,我們接下來的關注點是getApplicationEventMulticaster(),如何通過multicastEvent()方法將事件廣播出去呢?

2.2 什麼是ApplicationEventMulticaster

進入到核心關注點程式碼,getApplicationEventMulticaster()是什麼?

ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
  // ...略...  沒什麼重要資訊
  return this.applicationEventMulticaster;
}

程式碼很簡單,沒什麼邏輯,但是讓我們明白了getApplicationEventMulticaster()獲取到的物件是ApplicationEventMulticaster,那我們接下來搞懂這個東西(ApplicationEventMulticaster)是如何初始化?理清楚後可以弄清楚邏輯了。

輕輕鬆鬆翻到ApplicationEventMulticaster初始化的程式碼(給自己鼓個掌