Spring事件相關類關係源碼解析--Spring的事件機制源碼分析(二)

注意:該源碼分析對應版本爲spring5.1.x

1,概述

本篇開始分析Spring的事件機制源碼,因爲Spring的事件機制實質是觀察者(發佈訂閱)模式的實現,因此要想搞清楚Spring的事件機制,因此得知道觀察者模式是什麼。同時推薦閱讀下這篇文章的前奏文章,對於理解spring的事件機制非常有幫助,推薦我都另一篇翻譯的博文:

模仿Spring事件機制實現自定義事件驅動編程--Spring的事件機制源碼分析(一)

 

在開始正題前,先聊聊研究源碼的感受:研究源碼前那麼必須先搞清楚類與類之間的關係,比如某個接口有哪些實現類,某個父類有哪些子類,子類與子類之間的關係,這些類之間的關係捋清楚了,那麼再下手研究源碼就容易很多。總之不能一下子就進入源碼的某個細節,這樣子就會造成只見冰山一角而看不到全貌的感覺。

好了,下面開始進入正題,開始學習Spring的事件機制。因爲編碼一般都是面向接口編程,那麼我們先從事件機制的相關接口或抽象類開始分析。

Spring事件機制涉及的重要的類主要有以下四個:

  • ApplicationEvent:事件,該抽象類是所有Spring事件的父類,可攜帶數據比如事件發生時間timestamp。
  • ApplicationListener:事件監聽器,該接口被所有的事件監聽器實現,基於標準的java的EventListener接口實現觀察者模式。
  • ApplicationEventMulticaster:事件管理者,管理監聽器和發佈事件,ApplicationContext通過委託ApplicationEventMulticaster來         發佈事件
  • ApplicationEventPublisher:事件發佈者,該接口封裝了事件有關的公共方法,作爲ApplicationContext的超級街廓,也是委託             ApplicationEventMulticaster完成事件發佈。

2,Spring事件涉及類源碼分析

事件相關的主要接口類上面已經介紹完畢,下面來看下每個接口及其子類之間的關係。

2.1 ApplicationEvent

首先看下類圖如下:

                                                                                                 圖1

其接口代碼如下:

// 事件抽象類,這個是所有Spring事件的父類
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})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}


	/**
	 * Return the system time in milliseconds when the event happened.
	 */
	public final long getTimestamp() {
		return this.timestamp;
	}

}

ApplicationEvent類定義了一些屬性比如timestamp,這個表示事件的發生時間,因此可以通過事件來傳遞一些參數。

圖1是ApplicationEvent部分重要的子類關係圖,其中ApplicationEvent最重要的子類是ApplicationContextEvent抽象類,ApplicationContextEvent是spring容器Context生命週期事件的基類,ApplicationContextEvent的有四個子類,如下:

  • ContextRefreshedEvent:當spring容器context刷新時觸發
  • ContextStartedEvent:當spring容器context啓動後觸發
  • ContextStoppedEvent:當spring容器context停止時觸發
  • ContextClosedEvent:當spring容器context關閉時觸發,容器被關閉時,其管理的所有單例Bean都被銷燬。

以上四個事件就是spring容器生命週期的四個事件,當每個事件觸發時,相關的監聽器就會監聽到相應事件,然後觸發onApplicationEvent方法,此時就可以做一些容器,同時這些容器事件跟spring的後置處理器一樣,留給用戶擴展自定義邏輯,作爲暴露的擴展點。

以ContextRefreshedEvent事件爲例講解下相關監聽類,通過idea全局搜索"(ContextRefreshedEvent"關鍵字,得到以下截圖:

從上圖可以看到spring-webmvc模塊的FrameworkServlet,spring-context模塊的ScheduledAnnotationBeanPostProcessor,和spring-jms模塊的JmsListenerEndpointRegistry等類訂閱了ContextRefreshedEvent事件,那麼在容器刷新的時候這幾個類將會監聽到ContextRefreshedEvent事件,執行一些初始化邏輯。這一塊後面有時間再研究,TODO

下面粘貼下ApplicationContextEvent的四個子類的實現代碼,基本都是繼承ApplicationContextEvent父類,沒有什麼邏輯,更多是一個生命週期事件的標誌類。

public class ContextRefreshedEvent extends ApplicationContextEvent {

	// 當springcontext已經被初始化或者刷新的時候,創建該事件
	public ContextRefreshedEvent(ApplicationContext source) {
		super(source);
	}

}

public class ContextStartedEvent extends ApplicationContextEvent {

	// 當springContext已經啓動的時候,創建該事件
	public ContextStartedEvent(ApplicationContext source) {
		super(source);
	}

}

public class ContextStoppedEvent extends ApplicationContextEvent {

	// 當springContext已經停止時創建該事件
	public ContextStoppedEvent(ApplicationContext source) {
		super(source);
	}

}

public class ContextClosedEvent extends ApplicationContextEvent {

	// 當springContext關閉時創建該事件
	public ContextClosedEvent(ApplicationContext source) {
		super(source);
	}

}

2.2 ApplicationListener

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

ApplicationListener是所有事件監聽器的父接口,事件監聽器監聽某個事件必須要實現該接口。這裏值得注意的是ApplicationListener<E extends ApplicationEvent>接口的參數化類型<E extends ApplicationEvent>,這樣的話具體的監聽器實現該接口時可以指定特定的事件類,當傳入的事件向下轉型時不是該特定的事件時,此時會拋出類轉換異常。不過一般使用的時候會先判斷下該事件類型是否屬於某種事件,然後再執行相關邏輯,如下代碼:

    @Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationStartingEvent) {
			onApplicationStartingEvent((ApplicationStartingEvent) event);
		}
		else if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		else if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent((ApplicationPreparedEvent) event);
		}
		else if (event instanceof ContextClosedEvent
				&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
			onContextClosedEvent();
		}
		else if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}

由於ApplicationListener接口的具體實現類太多,因此就不貼類關係圖了。

2.2 ApplicationEventMulticaster

首先看下類圖,

ApplicationEventMulticaster接口功能主要用來廣播事件給所有listener,主要定義了增刪改監聽器和廣播事件的接口方法,代碼如下:

public interface ApplicationEventMulticaster {
    void addApplicationListener(ApplicationListener<?> var1);

    void addApplicationListenerBean(String var1);

    void removeApplicationListener(ApplicationListener<?> var1);

    void removeApplicationListenerBean(String var1);

    void removeAllListeners();

    void multicastEvent(ApplicationEvent var1);

    void multicastEvent(ApplicationEvent var1, @Nullable ResolvableType var2);
}

AbstractApplicationEventMulticaster是ApplicationEventMulticaster接口的抽象實現,提供最基本的監聽器註冊的方法。註冊監聽器時一般不允許相同監聽器註冊多個實例,因此使用Set集合,用於去重。然後實現廣播事件的具體實現沒有在這裏實現,而是交給子類SimpleApplicationEventMulticaster去實現。

AbstractApplicationEventMulticaster抽象類的關鍵代碼如下:

	// AbstractApplicationEventMulticaster.java

        /**
	 * 獲取事件監聽器的幫助類,擁有Set<ApplicationListener<?>>屬性
	 */
	private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
	/**
	 * ListenerRetriever緩存
	 * key:ListenerCacheKey  value:ListenerRetriever
	 */
	final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

        // 添加spring監聽器到ListenerRetriever的applicationListeners集合中
	public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.retrievalMutex) {
			// Explicitly remove target for a proxy, if registered already,
			// in order to avoid double invocations of the same listener.
			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
			if (singletonTarget instanceof ApplicationListener) {
				this.defaultRetriever.applicationListeners.remove(singletonTarget);
			}
			this.defaultRetriever.applicationListeners.add(listener);
			this.retrieverCache.clear();
		}
	}

        // 移除監聽器
	public void removeApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.retrievalMutex) {
			this.defaultRetriever.applicationListeners.remove(listener);
			this.retrieverCache.clear();
		}
	}

        // 移除所有監聽器
	public void removeAllListeners() {
		synchronized (this.retrievalMutex) {
			this.defaultRetriever.applicationListeners.clear();
			this.defaultRetriever.applicationListenerBeans.clear();
			this.retrieverCache.clear();
		}
	}

        // 利用defaultRetriever得到所有的監聽器
	protected Collection<ApplicationListener<?>> getApplicationListeners() {
		synchronized (this.retrievalMutex) {
			return this.defaultRetriever.getApplicationListeners();
		}
	}

    
    

根據上面代碼,大家注意到了AbstractApplicationEventMulticaster的增加,刪除和後去listeners是委託給其內部類ListenerRetriever去獲取的,因爲ListenerRetriever內部維護了監聽器的集合Set<ApplicationListener<?>>。下面看看ListenerRetriever這個內部類關鍵代碼:

private class ListenerRetriever {
		/**
		 * 監聽器集合
		 */
		public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

		public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

		private final boolean preFiltered;

		public ListenerRetriever(boolean preFiltered) {
			this.preFiltered = preFiltered;
		}

		/**
		 * 獲取所有的spring監聽器
		 * @return
		 */
		public Collection<ApplicationListener<?>> getApplicationListeners() {
			List<ApplicationListener<?>> allListeners = new ArrayList<>(
					this.applicationListeners.size() + this.applicationListenerBeans.size());
			allListeners.addAll(this.applicationListeners);
			if (!this.applicationListenerBeans.isEmpty()) {
				BeanFactory beanFactory = getBeanFactory();
				for (String listenerBeanName : this.applicationListenerBeans) {
					try {
						ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
						if (this.preFiltered || !allListeners.contains(listener)) {
							allListeners.add(listener);
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						// Singleton listener instance (without backing bean definition) disappeared -
						// probably in the middle of the destruction phase
					}
				}
			}
			if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
				AnnotationAwareOrderComparator.sort(allListeners);
			}
			return allListeners;
		}
	}

下面再來看下承擔廣播事件的SimpleApplicationEventMulticaster類的關鍵代碼:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
        // 執行廣播異步事件的線程
	@Nullable
	private Executor taskExecutor;
        // 廣播異步事件的線程時出現異常時的處理器
	@Nullable
	private ErrorHandler errorHandler;


	/**
	 * Create a new SimpleApplicationEventMulticaster.
	 */
	public SimpleApplicationEventMulticaster() {
	}


	@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		// 獲取執行異步任務的線程池,這裏異步要外部指定一個線程池,注入進來
		Executor executor = getTaskExecutor();
		// 遍歷每一個spring事件監聽器
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			// 若外部指定的線程池不爲null,則異步廣播事件
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			// executor爲空,則單線程同步廣播事件
			else {
				invokeListener(listener, event);
			}
		}
	}

	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
		// errorHandler不爲空的情況下,則會進入try...catch..代碼塊,這裏會對異步廣播事件發生的異常進行處理
		if (errorHandler != null) {
			try {
				// 這裏真正執行廣播事件的邏輯
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				// 處理異常
				errorHandler.handleError(err);
			}
		}
		// errorHandler爲空的情況下,則不對出現的異常進行處理
		else {
			doInvokeListener(listener, event);
		}
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			// 回調監聽器onApplicationEvent方法,執行監聽邏輯
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			// 若出現異常,這裏打印一些日誌或將異常繼續跑出去
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception and just log a debug message.
				Log logger = LogFactory.getLog(getClass());
				if (logger.isTraceEnabled()) {
					logger.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

}

 SimpleApplicationEventMulticaster是ApplicationEventMulticaster的實現類,承擔廣播所有事件給註冊的spring監聽器, 讓監聽器自己去決定哪些事件是自己感興趣的,監聽器們將會執行instanof來判斷是否是自己感興趣的事件。默認情況下,所有監聽器將會在調用線程中即單線程中同步阻塞執行,因此,若監聽器數量過多或某個監聽器執行時間過長 這將會導致spring容器啓動時間過長。不過SimpleApplicationEventMulticaster也提供了異步廣播時間的功能,通過taskExecutor來獲取線程池,然後多線程廣播事件,此外其還維護了一個errorHandler對象屬性,異常處理器,errorHandler主要用來當異步廣播事件時,若監聽器執行異常時,此時利用其來處理catch住的異常。

2.4 ApplicationEventPublisher

同樣,先來看下下面的類關係圖

可以看出所有Spring容器的父類接口ApplicationContext繼承了ApplicationEventPublisher這個接口,因此spring容器一般是具有廣播事件的功能。

下面來看下ApplicationEventPublisher的接口類代碼:

@FunctionalInterface
public interface ApplicationEventPublisher {
    default void publishEvent(ApplicationEvent event) {
        this.publishEvent((Object)event);
    }

    void publishEvent(Object event);
}

該接口封裝了發佈事件的公共方法,作爲ApplicationContext的超級接口,同事也是委託ApplicationEventMulticaster完成事件發佈。

下面再來看下Spring容器實現了ApplicationEventPublisher接口後是如何來發布事件的,此時得先來看下spring容器的父類接口ApplicationContext,因爲該接口繼承了ApplicationEventPublisher接口,因此讓spring容器具有了發佈事件的功能。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

        // 省略接口方法
        
        }

那麼spring容器是如何來發布事件的呢?前面已經講過ApplicationEventMulticaster接口,沒錯,spring容器context正是委託其來實現發佈事件的功能。因爲AbstractApplicationContext實現了ConfigurableApplicationContext接口,通過該接口最終實現了ApplicationEventPublisher接口,spring容器發佈事件的方法封裝在AbstractApplicationContext的publishEvent方法中,

下面直接看下相關代碼:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	/**
	 * 父類context
	 */
	@Nullable
	private ApplicationContext parent;

	/**
	 * 在multicaster setup前,發佈事件
	 */
	@Nullable
	private Set<ApplicationEvent> earlyApplicationEvents;

	// 發佈事件給所有事件監聽器,
	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 = 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 {
			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);
			}
		}
	}
}

最後,弄清楚該源碼機制後,自己再動手實操一下,推薦閱讀下面的實操文章:

spring 自定義事件發佈及監聽(簡單實例)

 

小結:這篇文章是本人第二篇源碼解析的文章,寫作速度仍然很慢,希望以後思路捋清楚後能快點寫完,加油。

 

參考:

https://spring.io/docs

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章