首先我們來看一個問題。就是我們在建立SpringBoot專案的時候會在對應的application.properties或者application.yml檔案中新增對應的屬性資訊,我們的問題是這些屬性檔案是什麼時候被載入的?如果要實現自定義的屬性檔案怎麼來實現呢?本文來給大家揭曉答案:
結合我們前面介紹的SpringBoot中的監聽事件機制,我們首先看下SpringApplication.run()方法,在該方法中會針對SpringBoot專案啟動的不同的階段來發布對應的事件。
處理屬性檔案載入解析的監聽器是 ConfigFileApplicationListener
,這個監聽器監聽的事件有兩個。
而我們進入SpringApplication.prepareEnvironment()方法中釋出的事件其實就是ApplicationEnvironmentPreparedEvent事件。進入程式碼檢視。
進行進入
繼續進入會看到對應的釋出事件:ApplicationEnvironmentPreparedEvent
結合上篇檔案的內容,我們知道在initialMulticaster中是有ConfigFileApplicationListener這個監聽器的。
那麼在此處觸發了設定環境的監聽器,後續的邏輯就應該進入對應的
接下來我們看下ConfigFileApplicationListener中具體的如何來處理組態檔的載入解析的。
根據邏輯我們直接進入onApplicationEnvironmentPreparedEvent()方法中。
系統提供那4個不是重點,重點是 ConfigFileApplicationListener 中的這個方法處理.
直接進入ConfigFileApplicationListener.postProcessEnvironment()方法。
在進入addPropertySources()方法中會完成兩個核心操作,1。建立Loader物件,2。呼叫Loader物件的load方法,
現在我們來看下在Loader構造器中執行了什麼操作。
通過原始碼我們可以發現在其中獲取到了屬性檔案的載入器、從spring.factories檔案中獲取,對應的型別是 PropertySourceLoader
型別。
而且在loadFactories方法中會完成物件的範例化。
到這Loader的構造方法執行完成了,然後來看下load()方法的執行。先把程式碼貼上
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
// 建立預設的profile 連結串列
this.profiles = new LinkedList<>();
// 建立已經處理過的profile 類別
this.processedProfiles = new LinkedList<>();
// 預設設定為未啟用
this.activatedProfiles = false;
// 建立loaded物件
this.loaded = new LinkedHashMap<>();
// 載入設定 profile 的資訊,預設為 default
initializeProfiles();
// 遍歷 Profiles,並載入解析
while (!this.profiles.isEmpty()) {
// 從雙向連結串列中獲取一個profile物件
Profile profile = this.profiles.poll();
// 非預設的就加入,進去看原始碼即可清楚
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
// 解析 profile
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
// 載入預設的屬性檔案 application.properties
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
然後我們進入具體的apply()方法中來檢視。
中間的程式碼都有註釋,主要是處理profile的內容。
首先是getSearchLocations()方法,在該方法中會查詢預設的會存放對應的組態檔的位置,如果沒有自定義的話,路徑就是 file:./config/ file:./ classpath:/config/ classpath:/ 這4個
然後回到load方法中,遍歷4個路徑,然後載入對應的屬性檔案。
getSearchNames()獲取的是屬性檔案的名稱。如果自定義了就載入自定義的
否則載入預設的application檔案。
再回到前面的方法
進入load方法,會通過前面的兩個載入器來分別載入application.properties和application.yml的檔案。
loader.getFileExtensions()獲取對應的載入的檔案的字尾。
進入loadForFileExtension()方法,對profile和普通設定分別載入
繼續進入load方法
開始載入我們存在的application.properties檔案。
在找到了要載入的檔案的名稱和路徑後,我們來看下資源載入器是如何來載入具體的檔案資訊的。
進入loadDocuments方法中,我們會發現會先從快取中查詢,如果快取中沒有則會通過對應的資源載入器來載入了。
此處是PropertiesPropertySourceLoader來載入的。
進入loadProperties方法
之後進入load()方法看到的就是具體的載入解析properties檔案中的內容了。感興趣的可以看下具體的邏輯,本文就給大家介紹到這裡了。