打個廣告
個人想寫《springboot源碼解析》這一系列很久了,但是一直角兒心底的知識積累不足,所以一直沒有動筆。
所以想找一些小夥伴一起寫這一系列,互相糾錯交流學習。
如果有小夥伴有興趣一起把這一系列的講解寫完的話,加下我微信:13670426148,我們一起完成,當交流學習。
後期還想寫一系列介紹rpc框架的,不過要再過一陣子了,先把springboot的寫完,堅持一週一更,週末更新
前言
上篇回顧
講了SpringApplication的實例化,其中最主要的是加載Spring的Listener和Initializer,Listener爲10個,Initializer爲6個,詳細查看 走心Springboot源碼解析: 一、SpringApplication的實例化
上篇寫完之後才發現之前用的是springboot1.5的版本,但是現在大家都是用springboot2.+,所以我找了個springboot2.0+ 的項目來進行解析,但是第一篇就還沒改變,到時候有空再更新,不過大致的內容還是差不多的。
這篇主要講run()方法,分成兩部分,這篇先講上半部分,主要聚焦在run()方法中涉及到的 事件-監聽器-廣播 機制,直到創建上下文部分(context = this.createApplicationContext())。
run()方法方法總覽
public ConfigurableApplicationContext run(String... args) {
//這是一個計時器,stopWatch.start()開始計時,到後面stopWatch.stop()停止計時。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//這個ConfigurableApplicationContext就是我們說的 上下文,後面preContext()的時候會詳細解析,這個是springboot中最重要的一個類了
ConfigurableApplicationContext context = null;
//自定義SpringApplication啓動錯誤的回調接口
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//此爲設置 System 裏面有一個properties的裏的值 "java.awt.headless" ,設置爲默認值true
this.configureHeadlessProperty();
//這個SpringApplicationRunListeners可跟前面的10個Listener不一樣,這裏面封裝了一個廣播。後面會講他的實例化過程
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//開始進行事件廣播,下面詳細解析
listeners.starting();
Collection exceptionReporters;
try {
//獲取初始化參數,就是我們運行時的初始化參數,比如“java -jar --port=8080” 其中port就是一個參數了
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//environment是運行時環境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//打印出springboot的標誌,這個可以自己選擇要打印輸出的文本.
Banner printedBanner = this.printBanner(environment);
//創建根上下文,這個方法特別重要,將是下章講解的重點
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//準備環境,下面先不講了。
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
廣播的封裝
SpringApplicationRunListeners:一個存SpringApplicationRunListener的集合,裏面有些方法,後續都會講到;
SpringApplicationRunListeners :
| SpringApplicationRunListener
|SimpleApplicationEventMulticaster
SimpleApplicationEventMulticaster
纔是真正的廣播,SpringApplicationRunListeners只不過是對其的一層封裝
SpringApplicationRunListeners的實例化過程
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
//這裏就不贅述了,META-INF/spring.factories下
//1. 獲取key爲SpringApplicationRunListener類路徑爲key 對應的 value
//2. 實例化value對應的類,最後得到的類是EventPublishingRunListener.class,,如圖1.1所示
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
圖1.1:
所以說得到的 SpringApplicationRunListeners 裏面有一個實例變量,內容是一個 EventPublishingRunListener 組成的 List 。
- EventPublishingRunListener繼承於SpringApplicationRunListener。
- SpringApplicationRunListeners裏面有一個實例變量,是多個EventPublishingRunListener組成的1個List。
SpringApplicationRunListener的結構
從命名我們就可以知道它是一個監聽者,那縱觀整個啓動流程我們會發現,它其實是用來在整個啓動流程中接收不同執行點事件通知的監聽者,SpringApplicationRunListener接口規定了SpringBoot的生命週期,在各個生命週期廣播相應的事件,調用實際的ApplicationListener類。
接下來看SpringApplicationRunListener
SpringApplicationRunListener接口規定了SpringBoot的生命週期
public class SpringApplicationRunListener{
//剛執行run方法時
void started();
//環境建立好時候
void environmentPrepared(ConfigurableEnvironment environment);
//上下文建立好的時候
void contextPrepared(ConfigurableApplicationContext context);
//上下文載入配置時候
void contextLoaded(ConfigurableApplicationContext context);
//上下文刷新完成後,run方法執行完之前
void finished(ConfigurableApplicationContext context, Throwable exception);
}
它定義了5個步驟:
- started() -> run方法執行的時候立馬執行;對應事件的類型是ApplicationStartedEvent,通知監聽器,SpringBoot開始執行
- environmentPrepared() -> ApplicationContext創建之前並且環境信息準備好的時候調用;對應事件的類型是ApplicationEnvironmentPreparedEvent), 通知監聽器,Environment準備完成
- contextPrepared -> ApplicationContext創建好並且在source加載之前調用一次;沒有具體的對應事件), 通知監聽器,ApplicationContext已經創建並初始化完成
- contextLoaded -> ApplicationContext創建並加載之後並在refresh之前調用;對應事件的類型是ApplicationPreparedEvent), 通知監聽器,ApplicationContext已經完成IoC配置價值
- finished -> run方法結束之前調用;對應事件的類型是ApplicationReadyEvent或ApplicationFailedEvent), 通知監聽器,SpringBoot啓動完成
EventPublishingRunListener的構造器
我們來看EventPublishingRunListener的構造器。還有他的兩個重要的方法:
EventPublishingRunListener類 實現了SpringApplicationRunListener,它具有廣播事件的功能。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
//這個是參數傳進來的,也就是SpringApplication.class
private final SpringApplication application;
//這個是初始化參數
private final String[] args;
//真正的廣 播
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
//新建立廣播器,這個廣播器特別重要,下面繼續解析
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//application.getListeners()就是獲取10個初始化的Listenter,具體可以參見上一篇瞭解哪10個Listener
Iterator var3 = application.getListeners().iterator();
while(var3.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var3.next();
//添加到內部的defaultRetriever裏,詳細代碼可以自己翻進去查看,這裏不拓展,
//後面需要根據事件類型推測有哪些監聽器需要被觸發,所以就得存把所有的監聽器先存起來,稱作一個註冊表吧
this.initialMulticaster.addApplicationListener(listener);
}
}
}
就是說,EventPublishingRunListener裏面有一個廣播器,結合上面的SpringApplicationRunListener接口聲明的方法,我們可以得到其機制大概是:
- run()方法是用來在整個啓動流程中接收不同執行點事件通知的監聽者,喚醒監聽者
- 而再進去就是調用EventPublishingRunListener的started()、environmentPrepared()等喚醒的,這裏面就會有根據不同的方法,started(),或者environmentPrepared()等,生成不同的事件。傳遞給廣播器。
- 而廣播器SimpleApplicationEventMulticaster initialMulticaster, 就從註冊表中(上文代碼說了註冊表的形成是那段代碼),根據Event的類型,找到那些監聽器是需要被觸發的,執行其 multicastEvent(ApplicationEvent event) 方法,內部是invokeListener(listener, event);
廣播器
multicastEvent() 方法就是廣播方法
SimpleApplicationEventMulticaster廣播出去,廣播出去的事件對象會被SpringApplication中的listeners屬性進行處理。
下面先解析一下廣播方法multicastEvent() 和 執行方法 invokeListener()
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//去註冊表中根據相應的事件,獲取Event對應的Listener。後面會簡略講講獲取的過程
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//最後的執行方法是這個,參數是“監聽器和事件”
this.invokeListener(listener, event);
}
}
}
//執行方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
//看多了源碼你們會發現,spring的源碼很喜歡這麼寫,一般invokeListener的時候不是真的invokeListener的真實過程
//而是會把真正的邏輯放到 doInvokeListener(do****,這裏就是doInvokeListener)中執行.
//1. 而在invokeListener中只是在真正處理方法前做一點數據封裝,
//2. 或者異常檢查
// 目的:個人感覺主要的目的是把真正處理過程的代碼縮減,使得真正的處理邏輯變得簡潔易懂,不會有多餘的代碼加重理解難度
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//這裏就很明白了,執行監聽器的方法
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, var6);
}
}
}
所以總結一下
兩種監聽器和廣播器的關係如下:
SpringApplicationRunListeners、SpringApplicationRunListener、SimpleApplicationEventMulticaster
(1) SpringApplicationRunListeners是SpringApplicationRunListener的封裝。pringApplicationRunListeners中包含多個SpringApplicationRunListener,
是爲了批量執行的封裝,SpringApplicationRunListeners與SpringApplicationRunListener生命週期相同,調用每個週期的各個SpringApplicationRunListener
然後廣播利用SimpleApplicationEventMulticaster進行廣播。
(2)SimpleApplicationEventMulticaster是封裝在SpringApplicationRunListener裏面的廣播器,
通過上面的3個特點可以看出SpringApplicationRunListener。springboot啓動的幾個主要過程的監聽通知都是通過他來進行回調。
流程圖如圖1.2所示
其中就可以看出他們之間的關係了。
開始跑第一個事件started
直接把代碼看到listeners.starting()
這一行
class SpringApplicationRunListeners {
public void starting() {
//這裏獲取SpringApplicationRunListeners裏面的List<SpringApplicationRunListener>
Iterator var1 = this.listeners.iterator();
//一般情況下,他只是一個,雖然這裏是個list,所以這個while裏面只會走1次
while(var1.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
//這個就進去到SpringApplicationRunListener裏面去了,而此時SpringApplicationRunListener的實現類是EventPublishingRunListener
listener.starting();
}
}
}
/**
** EventPublishingRunListener實現了SpringApplicationRunListener,所以就得實現裏面的starting(), environmentPrepared()等,貫穿整個springboot啓動流程,而starting()只是他其中最開始的一步,看最上面整體的代碼部分就知道了,後面還有environmentPrepared(), contextPrepared()等
**/
pubic class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
public void starting() {
//1. 廣播器執行廣播事件方法
//2. 參數是包裝出來的 ApplicationStartingEvent ,跟下面的environmentPrepared,contextPrepared方法比,他們的事件都類型都不同的
//(1) starting對應的是ApplicationStartingEvent
//(2) environmentPrepared對應的是 ApplicationEnvironmentPreparedEvent
//(3) ApplicationContextInitializedEvent對應的是 ApplicationContextInitializedEvent
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
//省略****
}
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
//開始進行廣播方法,因爲此時是staring()方法出發的,所以這裏的envent是在EventPublishingRunListener中new ApplicationContextInitializedEvent,
public void multicastEvent(ApplicationEvent event) {
//1. 第二個參數推斷出eventType,,在真正執行廣播
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) { //推斷出類型
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//從註冊表中根據event類型,獲得所有對應的監聽器,
//結果獲得的所有監聽器有:
//(1) LoggingApplicationListener、
//(2) BackgroundPreinitializer、
//(3) DelegatingApplicationListener
//(4) LiquibaseServiceLocatorApplicationListener
//(5)EnableEncryptablePropertiesBeanFactoryPostProcessor
//五種類型的對象。這五個對象的onApplicationEvent都會被調用。
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//直接看調用方法
this.invokeListener(listener, event);
}
}
}
//這就是最終的調用點,Listener的onApplicationEvent(ApplicationEvent) 方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, var6);
}
}
}
}
那麼這五個監聽器的onApplicationEvent都做了些什麼了,我這裏大概說下,細節的話大家自行去跟源碼。
(1) LoggingApplicationListener:初始化日誌系統,默認是logback,支持3種,優先級從高到低:logback > log4j > javalog
(2) BackgroundPreinitializer:啓動多個線程執行相應的任務,包括驗證器、消息轉換器等等
(3) DelegatingApplicationListener:此時什麼也沒做
(4) LiquibaseServiceLocatorApplicationListener:此時什麼也沒做
(5)EnableEncryptablePropertiesBeanFactoryPostProcessor:僅僅打印了一句日誌,其他什麼也沒做
對了,補充一點註冊器的代碼
喜歡就看,不喜歡就跳過,
// 返回所有的適合於ApplicationStartedEvent的監聽器集合
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = source != null ? source.getClass() : null;
//根據eventType和sourceType組裝一個key即是cacheKey
AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
//retriever裏面包裝了所有適合的監聽器。
AbstractApplicationEventMulticaster.ListenerRetriever retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
//下面是雙通道機制的單例模式寫法,直接看標註註釋的那行代碼即可
if (retriever != null) {
return retriever.getApplicationListeners();
} else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {
synchronized(this.retrievalMutex) {
retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
} else {
//當第一次執行的時候,創建一個 retriever,
retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
//下面進行詳細解析;
//直接在這裏獲取所有適合於ApplicationStartedEvent的監聽器集合,並使用retriever.applicationListeners.add(listener);,添加到retriever中,
Collection<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever);
//以cacheKey爲key,存到緩存中。
this.retrieverCache.put(cacheKey, retriever);
//返回所有的適合於ApplicationStartedEvent的監聽器集合
return listeners;
}
}
} else {
return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null);
}
}
//上面方法的解析,直接在這裏獲取所有適合於ApplicationStartedEvent的監聽器集合。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable
AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList();
LinkedHashSet listeners;
LinkedHashSet listenerBeans;
synchronized(this.retrievalMutex) {
//this.defaultRetriever.applicationListeners是所有默認的監聽器,就是那10個默認的監聽器,如下圖所示
listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
}
Iterator var7 = listeners.iterator();
while(var7.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var7.next();
//就是在這裏進行判斷的,具體怎麼判斷就不進去了,這個不是重點
if (this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
//........篇幅有限,這個其實不重要,直接略過即可
}
準備環境 prepareEnvironment
//todo: 這部分可以用“監聽器-事件-廣播” 解釋一下,下週末有時間再進行補充。
prepareEnvironment(listeners, applicationArguments);
加載SpringBoot配置環境(configurableEnvironment),如果是通過web容器發佈,會加載StandardEnvironment。將配置文件(Environment)加入到監聽器對象中(SpringApplicationRunListeners)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//如果environment不爲空直接返回 || 如果是web環境則直接實例化StandardServletEnvironment類 || 如果不是web環境則直接實例化StandardEnvironment類
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置環境信息
configureEnvironment(environment, applicationArguments.getSourceArgs());
//通知所有的監聽者,環境已經準備好了
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
總結
本篇主要講了“監聽器-事件-廣播”的涉及到的幾個類,展示了一個週期 starting()的運行過程。
再總結一下,流程如下:
- run()方法是用來在整個啓動流程中接收不同執行點事件通知的監聽者,喚醒監聽者
- 而再進去就是調用EventPublishingRunListener的started()、environmentPrepared()等喚醒的,這裏面就會有根據不同的方法,started(),或者environmentPrepared()等,生成不同的事件。傳遞給廣播器。
- 而廣播器SimpleApplicationEventMulticaster initialMulticaster, 就從註冊表中(上文代碼說了註冊表的形成是那段代碼),根據Event的類型,找到那些監聽器是需要被觸發的,執行其 multicastEvent(ApplicationEvent event) 方法,內部是invokeListener(listener, event);
下期預告
下期打算講一講, context的部分,這個內容就有很多了啊。。
參考內容: 大神寫的,分析很透徹,站在巨人的肩膀上: