SpringBoot項目啓動過程源碼終於整體捋了一遍(六)

截止上篇,關於SpingBoot的啓動流程終於看完了如何初始化SpringApplication,內容還是挺多的足足寫了5篇。並且過程中遺留了一個問題,即初始化SpringApplication的時候設置初始化器setInitializers()和設置監聽器setListeners()在啓動流程中起着什麼作用。

帶着遺留問題這篇開始看初始化完SpringApplication之後的run方法,先貼圖:

嚯,內容挺多的,這裏可以發現run()方法是待着參數args的,還記得當初只是想知道args和java -jar的參數是什麼關係,離答案應該不遠了。

首先看一二行:

 StopWatch一般用於計時,有start()後面必然有stop():

 然後用該類的getTotalTimeSeconds()方法可以拿到期間運行總時間:

看來我們平時程序中需要計時的時候不要再用寫兩個System.currentTimeMillis()一減了,這樣更優雅一點,而且StopWatch類中還有其他方法提供了更多的功能,可以去看一下學起來。

接着往下看:

前面先是定義了一個 ConfigurableApplicationContext類,這也是run()方法的返回值,然後是定義了一個異常報告的集合?行吧,定義好了就先放着吧,有什麼用後面再看。這個configureHeadlessProperty()乍一看不知道幹嘛的,可以看一下這個方法:

設置系統屬性,這個屬性是這樣子的:

Headless模式是系統的一種配置模式,在系統可能缺少顯示設備、鍵盤或鼠標這些外設的情況下可以使用該模式。我們的應用一般跑着服務器上的嘛,這哪來的外設,所以設置爲Headless模式也是應該的。

回到run()方法繼續往下看:

 先來看一下這個SpringApplicationRunListeners類是什麼,因爲後面還調用了它的starting()方法:

看來這個類就是個幹流水線活的,它的starting()、 environmentPrepared()包括其他方法都是遍歷這個監聽集合listeners,一個一個的調用一遍裏面元素SpringApplicationRunListener的對應方法,少了個s,就上了流水線。回到run()方法,看來關鍵是getRunListeners(args)方法拿到了哪些SpringApplicationRunListener應用運行監聽,再看這些監聽是幹什麼的,看一下這個getRunListeners()方法:

 初始化SpringApplicationRunListeners流水線的時候又看到了這個getSpringFactoriesInstances(),這個方法在第四篇(https://blog.csdn.net/weixin_42447959/article/details/105065584)分析過,就是加載在Spring.factories裏配置好的類嘛,這裏是取出鍵爲SpringApplicationRunListener的類。但是這裏調用這個方法的時候好像參數變多了,有type還有args,第四篇分析設置初始化器和設置監聽器的時候調用這個方法這兩個參數都爲空,不得不再看一遍這個方法:

這邊再看就主要關注parameterTypes和args這個兩個參數有什麼用了,和之前的關注點不一樣,也沒有白看,直接看createSpringFactoriesInstances()方法,看來這兩個參數是創建示例時候用的,記着這裏的parameterTypes是一個Class數組,裏面有SpringApplication.class和String[].class兩個元素,而args就是啓動命令裏面輸入的字符串了。看一下方法裏面:

大致可以看出來parameterTypes是爲了拿構造器,而args只是構造實例的時候用的,並沒有用它去幹嗎,就不想往下追究了,其實這時候可以猜測一下拿到的那些SpringApplicationRunListener應該有一個屬性是args,去驗證一下就行了,通過IDEA中類繼承實現拓撲圖功能可以看到SpringApplicationRunListener類只有一個實現類EventPublishingRunListener,在spring.factories中也可以得到驗證:

看一下這個類:

 果然有一個屬性args,並且SpringApplication也是它的屬性。到這裏需要把前面的理一下,從getRunListeners(args)拿到了這個SpringApplicationRunListeners流水線公司開始理,它裏面的各種方法都是流水線作業,遍歷調用真正的應用運行監聽SpringApplicationRunListener的對應方法,所以重點應該是SpringApplicationRunListener裏面各種方法幹了什麼,現在SpringApplicationRunListener接口在spring.factories中只配置了一個實現類即EventPublishingRunListener,所以在重點又成了這個裏面的starting()等各種方法是在幹什麼,流水線上執行的就是他們,看了一眼,這裏面好多方法,而且又是args又是SpringApplication的,作爲流水線上的真正且唯一的工人肯定幹了好多活,那就仔細看一下這個應用運行監聽。

先看一下EventPublishingRunListener的構造方法:

先是把SpringApplication和args初始化爲自己屬性,還初始化了一個SimpleApplicationEventMulticaster類,這是一個應用事件分發器,緊接着就是通過application.getListeners()拿到了之前初始化SpringApplication的時候設置的監聽器,並且SimpleApplicationEventMulticaster類的addApplicationListener()方法,就是告訴事件分發器有這麼多應用監聽。

看到這裏是又高興又不高興,高興的是之前遺留的問題解決了一半,終於知道初始化SpringApplication的時候設置的監聽器有什麼用了,不高興的是哪來的這麼多listener監聽聽來聽去的啊,這裏可以理一下:

可以看出初始化SpringApplication的時候設置監聽用的是AppliactionListener接口,將其理解爲應用監聽,實現類拿了一大堆。而後者在run()方法中拿應用運行監聽用的是SpringApplicationRunListener接口,將其理解爲應用運行監聽且只有一個實現類即EventPublishingRunListener,初始化該類的時候又需要用到前者AppliactionListener接口的一大堆實現類,即初始化應用運行監聽的時候需要拿到之前那一大堆的應用監聽,這就有點好理解了,應用運行監聽SpringApplicationRunListener就一個,裏面真正幹活的其實是這些應用監聽AppliactionListener,其實都是根據類名直譯過來的。

前面還說SpringApplicationRunListeners是流水線,SpringApplicationRunListener是真正工人呢,現在看來這個SpringApplicationRunListener也不是個幹活的工人,它就是個拉皮條的,它有一個SimpleApplicationEventMulticaster應用事件分發器可以讓真正的工人AppliactionListener應用監聽都幹起活來。

看完了EventPublishingRunListener的構造方法,就可以看看下面是怎麼通過一票的方法讓真正的工人AppliactionListener應用監聽幹不同的活,一個一個看。

先是starting()方法,聽着像是啓動應用的活,看一下:

可以看到先是利用SpringApplication和args構造了一個ApplicationStartingEvent對象,即應用啓動事件對象,看一下這個ApplicationStartingEvent類的繼承拓撲圖:

可以發現ApplicationStartingEvent應用啓動事件是沒有屬性的,args是父類SpringApplicationEvent的屬性,而SpringApplication則是在往上的EventObject的一個屬性(source)。

繼續往下看然後是調用了應用事件分發器的multicastEvent()方法,即分發事件方法,參數就是構建好的ApplicationStartingEvent對象,可以看一下這個方法:

這是一個重載方法,可以看到先是調用resolveDefaultEventType()方法拿到事件類型ResolvableType,不禁好奇這個事件類型是根據什麼定的,這次的事件是ApplicationStartingEvent應用啓動事件,繼續往下看拿到的ResolvableType是什麼,看這個resolveDefaultEventType()方法:

就這?好吧直接看forInstance()方法:

這次調用的instance是ApplicationStartingEvent顯然不是ResolvableTypeProvider類型,直接看後面的forClass方法,參數是ApplicationStartingEvent的Class類型:

可以看到是根據class類型構建了一個ResolvableType對象,大佬們能不能直接一點,那就繼續看一下怎麼構造這個ResolvableType的吧:

意思就是事件類型type就是參數class類型,舉個例子,這次的ApplicationStartingEvent應用啓動事件類型就是ApplicationStartingEvent.class,就這?這麼一頓截圖,大佬們也太含蓄了,好吧,好歹理清楚了繼續玩下看,前面的圖再貼一遍:

所以這次調用的事件是ApplicationStartingEvent,事件類型的type是ApplicationStartingEvent.class,然後調用了重載的multicastEvent()方法,方法一進來還有確認了一下事件類型ResolvableType對象不爲空,否則再去解析一遍事件類型,看來這個事件類型type挺重要的,看後面怎麼用吧。然後是通過getTaskExecutor()拿到了線程池Executor對象,線程池嘛,這裏就不看怎麼拿的了。

接下來是一個重點,有一個for遍歷,注意元素是ApplicationListener應用監聽,如果混淆了去前面理一理,這是真正幹活的工人,也是最初初始化SpringApplication的時候從spring.factories文件中加載進來的即是否還記得setListeners(),EventPublishingRunListener這個拉皮條的調用了SimpleApplicationEventMulticaster應用事件分發器的multicastEvent()分發事件方法,讓真正的工人AppliactionListener應用監聽都幹起活來,看,這裏開始把工人一個一個的逮出來了幹活了。可是前面說到初始化EventPublishingRunListener的時候不是已經從SpringApplication裏面拿到所有的ApplicationListener了嗎,爲什麼不直接遍歷而是調用了getApplicationListeners(event, type)方法,根據事件和事件類型又去篩選了一遍拿到了當前事件和事件類型的AppliactionListener應用監聽,看來讓工人幹活之前還要挑一挑符合要求的,工人也是分工種的嘛,這個方法再往下看有點寫不完了,舉例直接說結論吧,比如看我們熟悉的SqlSessionFactoryBean類:

沒想到吧它也是個幹活的ApplicationListener應用監聽,注意泛型是ApplicationEvent,還記得剛剛的getApplicationListeners(event, type)方法不?如果這裏的event是ApplicationEvent類型,這個應用監聽就會被拿到,即這個工人專業對口要被抓去幹活了,上面的繼承關係拓撲圖裏可以看出這個ApplicationEvent是一個父類,那豈不是很容易就被抓取幹活去。再舉個例子,這兒有一個自定義的類:

 即如果分發的事件類型是ApplicationEnvironmentPreparedEvent,那這個應用監聽就要去幹活了。

繼續往下看篩選後拿到所有的專業對口的ApplicationListener應用監聽後,遍歷調用了invokeListener()方法,這個方法圖就不貼了,裏面調用了doInvokeListener(),直接看這個方法:

果然,這裏調用了ApplicationListener的onApplicationEvent()方法,執行每個應用監聽真正的邏輯。

 每個ApplicationListener的實現類都會去重寫這個onApplicationEvent()方法,以完成自己的邏輯。

源碼擼到這裏整個過程已經清晰了,可以在理一遍,就從SpringApplicationRunListeners的starting()方法開始:

這個SpringApplicationRunListeners裏面有一個SpringApplicationRunListener集合,它的starting()方法其實就是遍歷調用集合中SpringApplicationRunListener元素的starting()方法,SpringApplicationRunListener只有一個從spring.factories裏讀出來的實現類叫EventPublishingRunListener,它裏面有一個事件分發器SimpleApplicationEventMulticaster,事件分發器有一個分發事件的方法multicastEvent(),這個方法的參數是構建好的不同的事件,例如starting()方法中構建的就是ApplicationStartingEvent應用啓動事件。方法裏面的邏輯是根據參數事件的事件類型篩選出所有符合事件類型的ApplicationListener應用監聽,然後遍歷執行它們重寫的onApplicationEvent()方法以執行各自的邏輯。理解起來就是ApplicationListener應用監聽成功監聽到了事件發佈器發佈的事件,然後執行了自己的邏輯,大佬們給類這麼起名字估計就是想讓我們這麼理解,然後擼源碼的過程中可沒有什麼監聽不監聽的騷操作,就是在一堆ApplicationListener裏挑一挑而已,主動的動作在代碼裏寫出來咋就成被動的呢?

這是starting()方法,他還有environmentPrepared()等方法也都是這樣,只不過不同的方法裏調用multicastEvent()分發了不同的事件,可以看圖:

看方法名貌似SpringBoot啓動時通過這種事件分發機制幹了好多大事,應用環境準備?上下文準備?上下文加載?這些方法後面應該會調用,重點就在spring.factories裏面配置了哪些ApplicationListener的實現類,這些應用監聽會在這幾個方法中被拿到執行onApplicationEvent()方法中自己的邏輯,這篇看了starting()方法,打斷點看了一下這些ApplicationListener的實現類被拿到了:

這是starting()方法裏面監聽到事件的監聽器的,其他方法之後再說,至於這些監聽器各自做了什麼,我看了一眼沒有用到參數args,這裏就不展開說了,可以去看一看。

這篇就到這裏了,例常總結一下,這篇看到了SpringApplicationRunListeners的starting()方法,發現了SpringBoot啓動時候藉助這種事件分發機制做了很多事,具體哪些事得看spring.factories裏面配置了哪些ApplicationListener的實現類。並且遺留問題解決了一半,SpringApplication中設置的監聽器就是在這裏用到了,但是設置的初始化器還不知道在哪裏用到,這個問題仍然遺留着。

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