我們在上一篇文章中簡單的說了一些SpringBoot配置屬性相關的一些内容,我們在這篇文章中接着上一篇的文章繼續進行分析。我們在上一篇文章中提到了這樣一個類:ConfigFileApplicationListener,從類名來看的話這是一個配置檔案應用監聽器,這個類主要的一個作用是在 refresh context之前解析預設的配置檔案。首先我們來看一下它的UML類圖:

ConfigFileApplicationListener實作了EnvironmentPostProcessor和SmartApplicationListener這兩個接口,從上圖中我們可以看到SmartApplicationListener其實是繼承了ApplicationListener這個接口的。SmartApplicationListener這個類相比于ApplicationListener多了兩個方法,一個是用來檢測是否支援給定的類型,一個是用來檢測得到的source type。EnvironmentPostProcessor這個接口的作用是在refresh application context之前定制化應用的環境配置資訊,需要說明的是,實作這個接口的類必須要在spring.factories進行配置。大家可以想一下這裡為什麼要這樣。既然ConfigFileApplicationListener也是一個監聽器類,那麼它肯定會監聽某些動作的發生,我們在之前的文章中說過,在org.springframework.boot.SpringApplication#run(java.lang.String… args)這個方法中會負責SpringBoot的整個啟動工作,在這裡有這樣的一段代碼:
順着listeners.starting();這個方法往下扒的話,你就會找到調用org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEvent這個方法的地方,這個調用鍊不算很負責,這裡就不再多說了。我們直接進入到ConfigFileApplicationListener#onApplicationEvent這個方法中:
但是啊但是,在onApplicationEvent中有兩個條件判斷,你通過listeners.starting()這個調用鍊一直跟蹤到這裡的話你會發現,這兩個條件都不滿足listeners.starting()的調用,那麼是在什麼地方觸發這個監聽事件的呢?在org.springframework.boot.SpringApplication#prepareEnvironment這個方法中,我們在上一篇文章中介紹了其中的一些代碼,這裡再看一段代碼:
org.springframework.boot.SpringApplicationRunListeners#environmentPrepared
org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared
從上面的代碼中我們可以看到在執行listeners.environmentPrepared(environment);
的時候,會調用ConfigFileApplicationListener#onApplicationEvent這個方法,并且所傳入的ApplicationEvent是ApplicationEnvironmentPreparedEvent,是以這裡會調用ConfigFileApplicationListener#onApplicationEvent中的org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEnvironmentPreparedEvent方法。我們進入到onApplicationEnvironmentPreparedEvent方法中看一下:
postProcessEnvironment的源碼如下:
在上面的代碼中,我們目前需要重點關注的就是ConfigFileApplicationListener#addPropertySources這個方法:
RandomValuePropertySource.addToEnvironment
上面這段代碼的意思就将RandomValuePropertySource添加到systemEnvironment的後面,到目前為止我們的MutablePropertySources#propertySourceList中的元素順序為:commandLineArgs、servletConfigInitParams 、servletContextInitParams 、jndiProperties 、systemProperties 、systemEnvironment、random。我們接着分析new Loader(environment, resourceLoader).load();這段代碼,這個代碼中的内容比較多,我們也不是全部都要關注,這裡我們先把Profile相關的内容先剝離出去。我們直接到這裡:
我們先來看上面标記為1)處的代碼:
我們接着看2)處的代碼:
是以上面while的這一段代碼我們可以簡化為這樣(極其簡化):