Spring boot屬性文件加載和生效順序深度分析

spring boot最核心的特性就是自動化配置,我們在學習spring boot的時候,首要需要了解它的自動化配置原理,其次是屬性文件的加載順序,我認爲這兩點是學習spring boot的重中之中。

網上介紹spring boot屬性文件的加載順序的文章很多,但都沒有從源碼上深入分析。
今天和大家一起通過源碼探究,spring boot屬性文件的加載順序。

這裏先說明一點:
先加載的屬性未必會生效,後加載的屬性未必一定會覆蓋先加載的屬性值。

說明:
加載指的是將屬性值讀取加載到spring容器中的過程;
生效指的是運用加載到的屬性值去初始化bean的過程。

屬性值加載的順序,和屬性值生效的順序沒有必然的關係。

爲什麼要分析?

主要是在實際項目開發過程中,由於屬性文件非常多,常常導致屬性文件配置混亂,
最常遇到的問題就是:
爲什麼明明配置了屬性,但項目啓動就是怎麼都不生效的問題?
所以,搞清楚屬性文件的讀取和生效的順序,對日常基於spring boot的開發過程中非常重要。

源碼分析

1、通過main啓動類,找到doInvokeListener()方法建立斷點,觀察ApplicationEvent中屬性源的變化情況:
說明:測試的工程註冊在eurake中,並從配置中心config中加載屬性。
在這裏插入圖片描述

2、最先加載的3個屬性源
在這裏插入圖片描述
Inlined Test Properties 單元測試屬性
systemProperties JVM系統屬性
SystemEnvironmentProperty 系統環境變量屬性

3、初始化ConfigFileApplicationListener監聽器時,新加載瞭如下數據源
在這裏插入圖片描述
configurationProperties
配置屬性源,這個屬性源比較特殊,他不會去自己加載配置屬性,而是將environment中已經加載到的屬性源按順序放入存放進來,並將自己放到propertySourceList的頭部first。(不是很好理解,個人感覺可能是一種緩存機制)
random 隨機數屬性源
springCloudClientHostInfo spring cloud客戶端host相關屬性源
bootstrap-dev.properties
bootstrap.properties
defaultProperties

4、加載配置中心屬性
在這裏插入圖片描述
可以發現,配置中心屬性源configService雖然是後面加載的,但是被放在了propertySourceList的頭部。

5、啓動完成後,environment中propertySourceList的順序
在這裏插入圖片描述
feign feign調用的屬性源
systemProperties java se運行時系統屬性
SystemEnvironmentProperty 系統環境變量屬性
configService 配置中心屬性源
configurationProperties 配置屬性源
Inlined Test Properties 單元測試屬性
random 隨機數屬性源
application-dev.propeties application-profile屬性源
application.propeties application屬性源
springCloudClientHostInfo spring cloud客戶端host相關屬性源
bootstrap-dev.propeties bootstrap-profile屬性源
bootstrap.propeties bootstrap屬性源
eureka/server.properties eureka服務屬性源
defaultProperties 默認屬性源

6、讀取屬性文件的源碼分析
在這裏插入圖片描述

springboot 啓動時,會初始化各種屬性源PropertySource,並把加載的屬性源存放到 enviroment 的propertySourceList中。我們在獲取屬性時,通過遍歷propertySourceList的屬性源去讀取屬性值,獲取對應屬性值就直接返回(先讀取先生效),所以在propertySourceList前面的屬性源會優先生效。

注意propertySourceList的類型是 CopyOnWriteArrayList ,即線程安全的ArrayList

7、開啓屬性獲取日誌,監控屬性獲取過程

logging.level.org.springframework.core.env=trace

在這裏插入圖片描述

通過注入Environment,查看執行屬性加載順序

@SpringBootTest
@Slf4j
class DemoApplicationTests {

    @Autowired
    Environment environment;

    @Test
    void getProperties() {
        System.out.println(environment.getProperty("spring.user.name"));
    }
}

properties和yml配置文件加載順序

一般在spring boot中最好統一隻使用一種類型的配置文件,避免出現配置混亂。
yml配置文件具有更好的格式,更強大的功能,但是對屬性文件的配置格式要求比較。格式出錯導致屬性讀取不到的問題比較難排查,且利用屬性的鍵值搜索指定屬性時,也不是很方便。properties屬性文件,好處就是簡單,不容易出現格式方面的問題,便於屬性查找。
假設一個項目在同一位置同時存在application.properties和application.yml文件,

且其中都含有相同的某個key,但value不同,如:

application.properties中:server.port=8001,

application.yml中:server.port=8888。

問題:springboot是否都加載這兩個配置文件?如果兩個文件有相同的key,取哪一個文件的value?

答: 都加載,且按properties→yml的順序加載。
在這裏插入圖片描述
在看到spring.factories中,配置加載器順序是先執行PropertiesPropertySourceLoader再到YamlPropertySourceLoader。

在ConfigFileApplicationListener獲取server.port這個key的value時候,可以發現兩配置文件全都加載進去了,且注意順序,application.properties文件在前。
在這裏插入圖片描述
getSource()方法獲取到兩個Source,先從application.properties文件中查找值,一旦找到立即返回,如果找不到再從application.yml中查找。

總結

1、spring boot中先讀取的屬性不一定先生效,生效的順序是根據bean初始化時,environment中屬性源propertySourceList的順序來決定的。

2、屬性源初始化讀取的順序和最後存放到environment中的propertySourceList的順序沒有必然關係。

    environment.getPropertySources().addFirst(source);
    environment.getPropertySources().addAfter(source1,source2);
    environment.getPropertySources().addBefore(source1,source2);
    environment.getPropertySources().addLast(source);

3、屬性文件的讀取順序大概是:
java se運行時系統屬性 ——》系統環境變量屬性——》
bootstrap.properties——》bootstrap-dev.properties ——》configService 配置中心屬性源——》application屬性源——》 application-dev屬性源

注意:
從屬性源的加載順序就可以看出,爲什麼我們在微服務裏面,在業務服務模塊進行配置中心配置的時候,一定要在bootstrap屬性文件中配置了。

4、項目中yml和properties這兩個格式的配置文件,一般只選用一種類型的配置文件。
如果同時配置兩種,會優先加載properties的配置文件,且properties的配置屬性會優先生效。

5、屬性文件的生效的順序大致是:
java se運行時系統屬性 ——》 系統環境變量屬性——》
configService 配置中心屬性——》application-dev屬性源——》 application屬性源——》bootstrap-dev.properties ——》bootstrap.properties
我們在java -jar啓動腳本中配置的屬性,屬於 java se運行時系統屬性,優先級最高。

6、如果項目中發現配置的屬性一直沒有生效,可以參考屬性源的生效順序,看看是否出現屬性覆蓋的問題。
更直接的方式是通過單元測試,執行environment.getProperty(“spring.user.name”),查看屬性源列表propertySourceList的順序,並通過打印日誌來判斷屬性值是從具體哪個屬性源加載到的。

希望看完本篇文章,當再被問到spring boot屬性文件的加載順序的時候,你可以胸有成竹的款款而談。

https://www.jianshu.com/p/256e6019349d

更多精彩,關注我吧。
圖注:跟着老萬學java

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