SpringBoot原始碼之屬性檔案載入原理剖析

2021-12-30 20:00:02

請新增圖片描述

SpringBoot原始碼之屬性檔案載入原理剖析

  首先我們來看一個問題。就是我們在建立SpringBoot專案的時候會在對應的application.properties或者application.yml檔案中新增對應的屬性資訊,我們的問題是這些屬性檔案是什麼時候被載入的?如果要實現自定義的屬性檔案怎麼來實現呢?本文來給大家揭曉答案:

image.png

1.找到入口

  結合我們前面介紹的SpringBoot中的監聽事件機制,我們首先看下SpringApplication.run()方法,在該方法中會針對SpringBoot專案啟動的不同的階段來發布對應的事件。

image.png

  處理屬性檔案載入解析的監聽器是 ConfigFileApplicationListener ,這個監聽器監聽的事件有兩個。

image.png

  而我們進入SpringApplication.prepareEnvironment()方法中釋出的事件其實就是ApplicationEnvironmentPreparedEvent事件。進入程式碼檢視。

image.png

進行進入

image.png

繼續進入會看到對應的釋出事件:ApplicationEnvironmentPreparedEvent

image.png

  結合上篇檔案的內容,我們知道在initialMulticaster中是有ConfigFileApplicationListener這個監聽器的。

image.png

  那麼在此處觸發了設定環境的監聽器,後續的邏輯就應該進入對應的

2.ConfigFileApplicationListener

2.1 主要流程分析

  接下來我們看下ConfigFileApplicationListener中具體的如何來處理組態檔的載入解析的。

image.png

  根據邏輯我們直接進入onApplicationEnvironmentPreparedEvent()方法中。

image.png

  系統提供那4個不是重點,重點是 ConfigFileApplicationListener 中的這個方法處理.

image.png

  直接進入ConfigFileApplicationListener.postProcessEnvironment()方法。

image.png

  在進入addPropertySources()方法中會完成兩個核心操作,1。建立Loader物件,2。呼叫Loader物件的load方法,

image.png

2.2 Loader構造器

  現在我們來看下在Loader構造器中執行了什麼操作。

image.png

  通過原始碼我們可以發現在其中獲取到了屬性檔案的載入器、從spring.factories檔案中獲取,對應的型別是 PropertySourceLoader型別。

image.png

  而且在loadFactories方法中會完成物件的範例化。

image.png

  到這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()方法中來檢視。

image.png

  中間的程式碼都有註釋,主要是處理profile的內容。

image.png

  首先是getSearchLocations()方法,在該方法中會查詢預設的會存放對應的組態檔的位置,如果沒有自定義的話,路徑就是 file:./config/ file:./ classpath:/config/ classpath:/ 這4個

image.png

image.png

  然後回到load方法中,遍歷4個路徑,然後載入對應的屬性檔案。

image.png

  getSearchNames()獲取的是屬性檔案的名稱。如果自定義了就載入自定義的

image.png

  否則載入預設的application檔案。

image.png

再回到前面的方法

image.png

進入load方法,會通過前面的兩個載入器來分別載入application.properties和application.yml的檔案。

image.png

loader.getFileExtensions()獲取對應的載入的檔案的字尾。

image.png

image.png

image.png

進入loadForFileExtension()方法,對profile和普通設定分別載入

image.png

繼續進入load方法

image.png

image.png

image.png

image.png

image.png

開始載入我們存在的application.properties檔案。

2.3 properties載入

  在找到了要載入的檔案的名稱和路徑後,我們來看下資源載入器是如何來載入具體的檔案資訊的。

image.png

進入loadDocuments方法中,我們會發現會先從快取中查詢,如果快取中沒有則會通過對應的資源載入器來載入了。

image.png

此處是PropertiesPropertySourceLoader來載入的。

image.png

image.png

進入loadProperties方法

image.png

之後進入load()方法看到的就是具體的載入解析properties檔案中的內容了。感興趣的可以看下具體的邏輯,本文就給大家介紹到這裡了。

image.png請新增圖片描述