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

上篇看了一部分的run()方法,先貼一下run()方法:

上篇看到了listeners.starting();介紹了SpringBoot啓動時的事件發佈機制,並且遺留了一個問題,即初始化SpringApplication時設置初始化器setInitializers()有什麼用,這篇繼續往下看:

利用參數args構建了一個DefaultApplicationArguments對象,顧名思義應該是應用參數,用到了args這個得好好看看:

可以看到args是被設置爲它的屬性,並且有一個getSourceArgs()方法可以拿到,那後面如果有地方調用這個方法得留點心,說不定可以知道args和java -jar的參數有什麼聯繫。

繼續往下看:

這裏是用剛剛的應用參數DefaultApplicationArguments對象和前面拿到的SpringApplicationRunListeners監聽流水線去拿一個可配置的環境ConfigurableEnvironment對象,看樣子是配置環境的時候又得去發佈點事件讓某些應用監聽幹活啊,看一下這個prepareEnvironment()方法,裏面就是去配置環境參數了:

上來先是創建了一個ConfigurableEnvironment對象,getOrCreateEnvironment()方法就不貼了,裏面分應用類型是SERVLET還是REACTIVE創建不同的環境變量對象,依據就是初始化SpringApplication時推斷出來的應用類型,這個第三篇提到過(https://blog.csdn.net/weixin_42447959/article/details/105055832)。然後看一下configureEnvironment()方法,這個方法開始配置環境:

this.addConversionService除非調用其set方法改爲false,不然默認就是true。這個ConversionService是Spring中用於運行時類型轉換的類,它可以將變量在字符串,數字,枚舉,集合,映射和其他常見類型之間進行轉換,這是一個接口,當然也就可以實現裏面的方法自定義轉換規則,可以看一下這個getSharedInstance():

典型的單例模式,看來類型轉換器只需要一個,一般這種什麼什麼shared的方法裏面都是單例,ApplicationConversionService是ConversionService接口的實現類,可以去看一下,平時如果需要自定義類型轉換規則的地方可以學起來,這裏拿到轉換器後也是用setConversionService()方法設置進了ConfigurableEnvironment環境對象,繼續往下看這個configurePropertySources()方法:

 哈!一眼就看到了到處都是commandLine命令行字樣,最初的疑問終於要找到答案了,這個方法是去拿環境中的配置屬性資源,配置屬性無非來自配置文件和命令行嘛,這裏是將配置文件裏的配置屬性addLast()添加在後面,後面又將命令行中的配置屬性addFirst添加在前面,那這可以理解爲命令行中的配置優先級比較高嗎?想想好像確實是這樣,比如在yml配置文件中配置spring.profiles.active=dev,然後java -jar的時候加啓動參數--spring.profiles.active=prod,最後生效的配置文件還是prod生產環境的。其他的圖不貼了,有興趣的可以去看一看,貼一下怎麼利用命令行參數args去初始化SimpleCommandLinePropertySource命令行屬性資源對象的:

就這?那就看一下parse()方法:

終於找到答案了,java -jar後面的啓動參數字符串就是在這裏被解析爲配置的,怪不得參數要用"--"開頭。

剛看的configurePropertySources()方法是拿到配置屬性資源,主要有兩個來源即配置文件和命令行,繼續往下看configureProfiles()方法:

這個方法的註釋寫的很明白,即"爲這個應用程序配置哪些配置文件是活動的(默認情況下是活動的)",也就是激活哪些配置文件,上一個方法可能是拿到了一大堆配置文件,這個方法就是真正決定哪些配置有效了,這個getActiveProfiles()方法最終是調用了doGetActiveProfiles(),大佬慣用的手法,直接看這個方法:

 看這個ACTIVE_PROFILES_PROPERTY_NAME常量:

根據這個key拿到了值,之後的事不看源碼也能知道怎麼回事了,到這裏就真相大白了。 

這一頓扯的有點遠,繼續回到prepareEnvironment()方法,再貼一遍這個方法:

剛剛是看完了configureEnvironment()方法,總結一下就是給環境對象ConfigurableEnvironment喂地肥肥的,給了他類型轉化器,最主要的還是把一堆配置加入環境對象中,方法名配置環境嘛,應該的。

繼續往下看:

很熟悉吧,這個上篇分析過了(https://blog.csdn.net/weixin_42447959/article/details/105111675),最終是事件分發器發佈了ApplicationEnvironmentPreparedEvent應用環境準備事件,bean容器中對應的ApplicationListener監聽到了以後會去執行自己的邏輯,具體哪些監聽器監聽到了,打了一下斷點是這些:

當然裏面部分不是spring框架中的,例如PuristBootBanner就是自定義的監聽器,具體這些監聽幹了什麼有時間再仔細看看。

回到文章開頭的run()方法中繼續往下看這行:

現在的environment今非昔比了,環境中有一大堆配置了,看一下這個方法要幹嘛:

這裏又是大佬慣用的一個手法,先System.getProperty(),再System.setProperty(),也是以防萬一吧,看一下這個屬性的鍵是哪個:

上面那段代碼把這個屬性設置爲true,好像很少在配置文件中寫"spring.beaninfo.ignore"這個配置,也是看了上面的一大堆註釋,這個屬性作用於是否跳過對BeanInfo類的搜索,註解中說如果改爲true即開啓之後會減少應用啓動和加載的開銷。首先這個BeanInfo類顧名思義可以拿到bean容器中的bean信息,用法如圖:

 但是這個註釋中說默認爲false是考慮到所有的BeanInfo元數據類?不懂,感覺是應該設置爲true,這裏SpringBoot啓動的時候,除非在配置文件中配置false,也是默認把這裏設置爲true,也是爲了減小不必要的開銷吧。

回到文章開頭的run()方法中繼續往下看這行:

這就是啓動Spring的時候那個大大的Spring圖案打印的方法,看一下這個方法:

 可以看出是否打啓動Banner有一個this.bannerMode控制,項目中如果需要打印自定義的Banner,就需要將這個布爾型變量設置爲false,然後自定義一個ApplicationListener去打印自定義Banner。

這篇就到這裏吧,在run()方法中就走了兩行代碼,主要分析了啓動過程中如何將配置屬性加入環境對象中,總之是把ConfigurableEnvironment這個對象喂地肥肥的,告訴了它不少東西。這篇最大的收穫還是找到了最初的問題答案,即args和java -jar後面的啓動參數有什麼關係,就是在給環境對象配置屬性的時候用到了。同時文章開頭的問題依然遺留着,即初始化SpringApplication時設置初始化器setInitializers()有什麼用,遺留了好幾篇了,不過之後終歸會找到答案的吧。

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