文章目錄
- 一、介紹
- 二、演示環境
- 三、項目演示
- 1. 配置文件
- 2. 導入配置
- 3. 檢測配置屬性
- 四、應用場景
- 五、源碼解析
- 1. ConfigTreeConfigDataLocationResolver
- 2. ConfigTreeConfigDataLoader
- 六、總結
一、介紹
相信絕大多數使用springboot開發項目的朋友們在添加配置時,通常都是通過以下幾種方式:
- 在classpath下添加application.yml或application.properties配置文件,或通過
spring.config.location
指定配置文件位置。 - 通過
spring.config.additional-location
指定額外的配置文件位置。 - 通過
spring.config.import
導入指定位置的配置文件。
但無論通過哪種方式,其配置的形式都是通過在配置文件中通過key - value
的形式添加具體配置的,且配置文件類型為yaml
或properties
。如下所示:
-
properties文件內容示例
key1 = value1
-
yaml文件內容示例
key1: value1
其中key1
作為配置名,value1
作為配置值。
今天給大家介紹另一種配置形式,該配置使用文件名
作為配置名,文件內容
作為配置值。
如文件名為username
的內容如下:
admin
文件名為password
的內容如下:
123456
二、演示環境
本演示項目的環境如下:
- java:1.8
- springboot:2.4.3
三、項目演示
本項目演示的是,在指定目錄中添加配置文件,并以文件名為key,文件內容為value;然后在application.yml
配置文件中通過spring.config.import
指定configtree
將目錄中的所有配置文件添加到項目的環境中,并通過placeholder${}
的形式獲取配置。
1. 配置文件
我們在本地文件系統中添加配置文件,其目錄結構如下所示
/etc/app/config/admin/usernamepassworddb/usernamepasswordnacos/usernamepassword
各個配置文件內容如下所示
-
/etc/app/config/admin/username
admin
-
/etc/app/config/admin/password
123456
-
/etc/app/config/db/username
mysql
-
/etc/app/config/db/password
123456
-
/etc/app/config/nacos/username
nacos
-
/etc/app/config/nacos/password
nacos
2. 導入配置
在application.yml
配置文件中添加配置spring.config.import
。
注意,當我們要添加以文件名為key,文件內容為value的配置文件時,必須在路徑前添加前綴configtree:
,且路徑最后以/
結尾。另外,該路徑支持*
通配符。
spring:config:import:- configtree:/etc/app/config/
如上所示,springboot將讀取路徑/etc/app/config/
(包括子目錄)中的所有文件,并以文件名為key,文件內容為value。
3. 檢測配置屬性
當我們按照上面示例配置時,由于在application.yml
中配置的spring.config.import
目錄為/etc/app/config/
,因此我們可以通過admin.username
、admin.password
、db.username
、db.password
、nacos.username
和nacos.password
獲取對應文件內容的值。
下面我們啟動項目對其進行檢驗。
啟動項目:
輸出:
四、應用場景
看到這里,想必很多小伙伴雖然知道springboot如何通過spring.config.import
+ configtree
來讀取以文件名為key,文件內容為value的配置,但是這種配置方式使用起來并不方便,且一個文件僅對應一個配置屬性,那如果需要大量配置豈不是要創建大量文件?
其實,使用該配置方式和使用application.yml
方式應該是相輔相成的,兩者應當配合使用。
當我們在云平臺(比如docker)上運行應用程序時,有時需要讀取容器提供的配置值。而我們多數情況下都是通過該容器的環境變量來獲取所需的配置,但是如果我們可能會頻繁修改該環境變量或該變量需要加密時,就可能暴露出它的缺點了。因為容器的環境變量是在創建鏡像的時候就確定的,當我們需要修改該環境變量時就意味著已經創建的容器需要刪除了。
所以我們可以通過容器掛載卷的方式,將該環境變量保存在文件中,通過掛載卷將配置文件掛載到容器中。
五、源碼解析
在前面我們源碼分析springboot如何創建并配置環境3 - 配置擴展屬性2文章中,簡單介紹過springboot通過ConfigTreeConfigDataLocationResolver
和StandardConfigDataLocationResolver
兩種配置文件位置解析器來解析配置文件的位置,然后通過ConfigTreeConfigDataLoader
和StandardConfigDataLoader
來加載對應配置文件中的配置內容。
因此結合本文重點,我們應主要關注ConfigTreeConfigDataLocationResolver
和ConfigTreeConfigDataLoader
是如何解析配置文件的位置并從文件中讀取配置內容的。
1. ConfigTreeConfigDataLocationResolver
首先我們查看ConfigTreeConfigDataLocationResolver
是如何解析出配置文件目錄的,主要分兩步:①判斷配置的路徑是否滿足解析的條件,②解析配置文件的位置。
-
判斷配置的路徑是否滿足解析的條件
該判斷邏輯通過
isResolvable()
方法完成,主要判斷依據就是配置的spring.config.import
值是否包含configtree:
前綴,如果包含,則滿足條件。private static final String PREFIX = "configtree:";@Override public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {return location.hasPrefix(PREFIX); }
-
解析配置文件的位置
該邏輯通過方法
resolve()
完成,其目的是根據配置的spring.config.import
目錄轉換為該目錄下文件的資源。@Override public List<ConfigTreeConfigDataResource> resolve(ConfigDataLocationResolverContext context,ConfigDataLocation location) {try {return resolve(context, location.getNonPrefixedValue(PREFIX));}catch (IOException ex) {throw new ConfigDataLocationNotFoundException(location, ex);} }private List<ConfigTreeConfigDataResource> resolve(ConfigDataLocationResolverContext context, String location)throws IOException {// 目錄必須以“/結尾”Assert.isTrue(location.endsWith("/"),() -> String.format("Config tree location '%s' must end with '/'", location));// 如果目錄不是通配符的形式,則直接根據該目錄獲取該目錄下文件的資源集合。if (!this.resourceLoader.isPattern(location)) {return Collections.singletonList(new ConfigTreeConfigDataResource(location));}// 如果目錄是通配符的形式,則對其進一步處理,獲取該目錄下文件的資源集合。Resource[] resources = this.resourceLoader.getResources(location, ResourceType.DIRECTORY);List<ConfigTreeConfigDataResource> resolved = new ArrayList<>(resources.length);for (Resource resource : resources) {resolved.add(new ConfigTreeConfigDataResource(resource.getFile().toPath()));}return resolved; }
2. ConfigTreeConfigDataLoader
然后我們分析ConfigTreeConfigDataLoader
是如何根據配置文件資源加載其內容的。
在加載配置屬性中,我們看到該方法主要分兩步,①根據文件資源獲取文件路徑path,②根據文件路徑獲取該文件中的配置。我們在方法結束時添加斷點,然后啟動項目,讓代碼運行到斷點處。如下圖所示,我們發現springboot已經按照預期將各個配置文件讀取成功了。
任意點擊其中一個元素,可以看到配置文件中的內容也已經被加載了
最后將其封裝到ConfigData
對象中返回。
六、總結
- 通過
spring.config.import
+configtree:
前綴的方式,加載以文件名為key、文件內容為value的配置屬性。 configtree:
應以/
結尾。- 適用于代替在云平臺中讀取加密的系統環境變量的場景。
紙上得來終覺淺,絕知此事要躬行。
————————————————我是萬萬歲,我們下期再見————————————————