Springboot事件監聽機制——事件發佈(二)

Spring中的事件監聽
前面我們大致瞭解了一下事件監聽,我們接着來看看Spring中的事件監聽是如何使用的。這裏我選用了springboot 1.5.7的源碼來學習。

在啓動類中執行SpringApplication.run()後,首先會執行SpringApplication類的initialize(Object[] sources)方法,然後初始化initializers和listeners兩個集合,initializers集合存放的是springboot從配置文件自動裝配的6個ApplicationContextInitializer對應的類,listeners集合存放的是從配置文件自動裝配的10個ApplicationListener對應的類。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
這個加載方式是通過讀取配置文件中類的路徑,然後利用反射生成的對象。具體方法如下:
在這裏插入圖片描述

接下來,我們看run(String… args)方法中的執行內容:
在這裏插入圖片描述
首先會通過getRunListeners(String[] args)方法去從配置文件讀取key=SpringApplicationRunListener對應的類名然後通過反射生成對象,這裏就一個EventPublishingRunListener對象。

接着執行listeners.starting();方法。這裏會調用EventPublishingRunListener的starting()方法,查看源碼,我們看到這裏是調用一個廣播器發送廣播的方法
在這裏插入圖片描述
繼續跟蹤,執行到了SimpleApplicationEventMulticaster類中,通過名字我們可以知道這是一個廣播器,用於專門發送事件的。
在這裏插入圖片描述
最終在這裏調用發送事件的方法。這裏的eventType是ApplicationStartedEvent。通過getApplicationListeners(event, type)可以得到4個listener:
LoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
LiquibaseServiceLocatorApplicationListener

注意:這裏有個判斷,如果有Executor不爲空,就說明是開啓線程異步發送的,否則就是同步發送。
我們如何開啓異步發送,這個我們後面說。

我們接着看run()方法中的prepareEnvironment(listeners,applicationArguments);方法:
在這裏插入圖片描述
看到這裏有個listeners.environmentPrepared()方法,跟蹤代碼後發現它調用EventPublishingRunListener的environmentPrepared()方法,它同樣也是執行發送廣播的方法:
在這裏插入圖片描述
它發送廣播時eventType爲ApplicationEnvironmentPreparedEvent。通過getApplicationListeners(event, type) 得到以下7個listener:
ConfigFileApplicationListener
AnsiOutputApplicationListener
LoggingApplicationListener
ClasspathLoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
FileEncodingApplicationListener

繼續看run()方法中的prepareContext()方法,我們看到這裏會執行2個方法listeners.contextPrepared(context); 和 listeners.contextLoaded(context);
跟蹤代碼後發現contextPrepared 沒有執行任何代碼,所以我們不管它。
在這裏插入圖片描述
我們看 listeners.contextLoaded(context); 它調用EventPublishingRunListener的contextLoaded()方法,首先會把我們前面加載得到的10個ApplicationListener對應的對象放到context容器中。接着再發送廣播,這裏的eventType爲ApplicationPreparedEvent,通過getApplicationListeners(event, type) 得到以下4個listener:
ConfigFileApplicationListener
LoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
在這裏插入圖片描述

接着看run()方法中的refreshContext(context);方法,它裏面調用的refresh()方法,看過spring源碼的都應該對這個refresh都應該不陌生。它是spring中很重要的一個方法。

在這裏插入圖片描述

這裏我們主要看關於listener事件的處理。我們先看initApplicationEventMulticaster():
在這裏插入圖片描述
這裏主要是獲取SimpleApplicationEventMulticaster applicationEventMulticaster對象。

接着看registerListeners()方法,這裏會分別把容器中的listener存放在defaultRetriever對象的applicationListeners集合中,defaultRetriever是定義在抽象類AbstractApplicationEventMulticaster中的成員,用來保存所有事件監聽器及其beanName
在這裏插入圖片描述
在這裏插入圖片描述

再執行finishRefresh(),這裏它會先進入EmbeddedWebApplicationContext類的finishRefresh(),然後調用super.finishRefresh();去執行AbstractApplicationContext類的finishRefresh()。

EmbeddedWebApplicationContext類:在這裏插入圖片描述

AbstractApplicationContext類:
在這裏插入圖片描述
在AbstractApplicationContext類的finishRefresh()中它會執行publishEvent()方法去SimpleApplicationEventMulticaster類中發送廣播,此時的type爲ContextRefreshedEvent,getApplicationListeners(event, type) 有如下6個值:
ConfigurationPropertiesBindingPostProcessor
DelegatingApplicationListener
AutoConfigurationReportLoggingInitializer
ClearCacheApplicationListener
SharedMetadataReaderFactoryContextInitializer
ResourceUrlProvider

發送完後又會到EmbeddedWebApplicationContext類的finishRefresh(),此時如果我們得到了一個嵌入式的容器(這裏應該指tomcat),那麼我們同樣會去SimpleApplicationEventMulticaster類中發送廣播,此時的type爲EmbeddedServletContainerInitializedEvent,getApplicationListeners(event, type) 有如下2個值:
DelegatingApplicationListener
ServerPortInfoApplicationContextInitializer

執行完返回到run()方法中,接着執行listeners.finished(context, null),這裏會執行到AbstractApplicationContext類中的publishEvent()方法,最終也會去SimpleApplicationEventMulticaster類中發送廣播,此時的type爲ApplicationReadyEvent,getApplicationListeners(event, type) 有如下3個值:
BackgroundPreinitializer
DelegatingApplicationListener
SpringApplicationAdminMXBeanRegistrar

至此我們springboot在啓動時,事件監聽就全部執行完了。容器也啓動成功了。

Spring事件彙總

事件Event 說明
ContextRefreshedEvent 當容器被實例化或refreshed時發佈.如調用refresh()方法, 此處的實例化是指所有的bean都已被加載,後置處理器都被激活,所有單例bean都已被實例化, 所有的容器對象都已準備好可使用. 如果容器支持熱重載,則refresh可以被觸發多次(XmlWebApplicatonContext支持熱刷新,而GenericApplicationContext則不支持)
ContextStartedEvent 當容器啓動時發佈,即調用start()方法, 已啓用意味着所有的Lifecycle bean都已顯式接收到了start信號
ContextStoppedEvent 當容器停止時發佈,即調用stop()方法, 即所有的Lifecycle bean都已顯式接收到了stop信號 , 關閉的容器可以通過start()方法重啓
ContextClosedEvent 當容器關閉時發佈,即調用close方法, 關閉意味着所有的單例bean都已被銷燬.關閉的容器不能被重啓或refresh
RequestHandledEvent 這隻在使用spring的DispatcherServlet時有效,當一個請求被處理完成時發佈
SpringApplicationEvent 與SpringApplication相關的ApplicationEvent的基類
ApplicationStartedEvent 該事件儘可能早地發佈,只要SpringApplication已啓動。它在Environment或ApplicationContext有效之前,但是在ApplicationListener已經註冊後發佈。不過要小心使用它的內部,在這個早期階段狀態太多,因爲它可能在生命週期的後期被修改
ApplicationStartingEvent ApplicationStartingEvent是ApplicationStartedEvent的父類,ApplicationStartedEvent已經過時了,在1.5版本開始支持ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent 當SpringApplication啓動時,且Environment首先可用於檢查和修改
ApplicationPreparedEvent 當SpringApplication啓動並且ApplicationContext已完全準備好,但未刷新時發佈。Bean定義將被加載,Environment已準備好在此階段使用
EmbeddedServletContainerInitializedEvent 在上下文刷新和EmbeddedServletContainer已就緒後發佈。用於獲取正在運行的服務器的本地端口。正常情況下,它會被啓動,但是監聽器可以自由地檢查服務器,如果願意,可以停止和啓動它。
ApplicationFailedEvent 啓動失敗時由SpringApplication發佈的事件
ApplicationReadyEvent 儘可能晚地發佈事件,以指示應用程序已準備好爲請求提供服務。事件的源是SpringApplication本身,但請注意不要修改其內部狀態,因爲屆時所有初始化步驟都將完成。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章