SpringBoot讀取配置優(yōu)先級順序的方法詳解
引言
Spring Boot作為一種輕量級的Java應(yīng)用程序框架,以其開箱即用、快速搭建新項目的特性贏得了廣大開發(fā)者的青睞。其核心理念之一就是簡化配置過程,使開發(fā)者能夠快速響應(yīng)復(fù)雜多變的生產(chǎn)環(huán)境需求。為了實現(xiàn)這一點,Spring Boot支持豐富的外部化配置機制,允許應(yīng)用程序根據(jù)不同的部署環(huán)境靈活加載相應(yīng)的配置屬性,而無需修改代碼本身。
在Spring Boot生態(tài)系統(tǒng)中,配置屬性可以從各種來源獲取,比如:Java屬性文件、YAML文件、環(huán)境變量、命令行參數(shù)等。這些配置屬性能夠在運行時動態(tài)注入到Bean中,極大地提高了系統(tǒng)的可擴展性和可配置性。然而,為了確保一致性和防止配置沖突,Spring Boot在加載這些外部配置時遵循一套嚴格的優(yōu)先級順序。掌握這套優(yōu)先級規(guī)則至關(guān)重要,因為它直接影響著最終生效的配置屬性值,進而決定了應(yīng)用程序的行為模式。
本文將深入探討Spring Boot加載外部配置屬性的優(yōu)先級規(guī)則,詳盡梳理各個配置源的加載順序,并結(jié)合實際應(yīng)用場景舉例說明,以便我們能夠更高效地管理和遷移配置,確保在不同環(huán)境下應(yīng)用程序都能穩(wěn)定、準確地運行。
Spring Boot外部化配置概述
Spring Boot的核心價值之一在于其強大的外部化配置能力,這使得應(yīng)用程序能夠在不改變代碼的情況下適應(yīng)不同的運行環(huán)境。外部化配置意味著將應(yīng)用程序的關(guān)鍵配置信息移至應(yīng)用程序代碼之外,便于根據(jù)不同環(huán)境(如開發(fā)、測試、生產(chǎn)等)進行定制化配置。Spring Boot提供了多樣化的外部配置源以及便捷的屬性注入方式,使得這種配置機制變得異常靈活且易于管理。
多樣化配置源
Spring Boot支持多種類型的外部配置源,主要有如下幾個方面:
Properties文件:
通常使用.properties
格式,采用鍵值對的形式存儲配置信息。
server.port=8080 logging.level.root=DEBUG
YAML文件:
相較于傳統(tǒng)的properties文件,YAML提供了更直觀、層次更分明的數(shù)據(jù)結(jié)構(gòu),尤其適合存儲復(fù)雜配置。使用.yml
格式。
server: port: 8080 logging: level: root: DEBUG
環(huán)境變量:
操作系統(tǒng)級別的環(huán)境變量可以被Spring Boot識別并作為配置源,這對于云環(huán)境和容器化部署尤為實用。
命令行參數(shù):
啟動Spring Boot應(yīng)用時,可以傳入命令行參數(shù)(以--
開頭)直接覆蓋已有配置。
屬性注入方式
在Spring Boot中,外部配置的屬性值可以通過以下幾種方式方便地注入到Bean中。
@Value
注解:可以直接在字段或方法參數(shù)上使用此注解,將配置屬性值注入到目標對象中。Environment
接口:Spring框架提供的環(huán)境抽象類,可以用來查詢所有已加載的配置信息。@ConfigurationProperties
注解:用于綁定一組相關(guān)配置到一個專門的Java Bean中,提供更結(jié)構(gòu)化的配置管理方式。
配置加載優(yōu)先級
Spring Boot對來自不同配置源的同名屬性可以按照一定的優(yōu)先級順序進行覆蓋。其優(yōu)先級從上到下變高,即后面的配置源將覆蓋前面的配置源。
- 默認屬性(通過
SpringApplication.setDefaultProperties
方法設(shè)置) @PropertySource
注解加載的配置- Config Data(配置數(shù)據(jù))(本地文件系統(tǒng)或打包在jar中的application.properties和application-{profile}.properties)
- 特殊屬性源(如隨機數(shù)生成器、環(huán)境變量、系統(tǒng)屬性、JNDI屬性等)
- Servlet容器相關(guān)的初始化參數(shù)
- SPRING_APPLICATION_JSON格式的環(huán)境變量或系統(tǒng)屬性
- 命令行參數(shù)
- 測試相關(guān)的屬性注入方式(如
@SpringBootTest
、@DynamicPropertySource
和@TestPropertySource
)
以上優(yōu)先級順序來源于官網(wǎng):Spring Boot Reference Documentation
Spring Boot配置加載順序詳解
默認屬性
默認屬性是指Spring Boot框架內(nèi)置的一些默認配置值??梢栽趧?chuàng)建SpringApplication
實例時,通過調(diào)用setDefaultProperties(Map<String, Object> defaultProperties)
方法來提供一組默認屬性,這些屬性將被優(yōu)先加載,但是也會被其他配置覆蓋。
@SpringBootApplication public class SpringBootBaseApplication { public static void main(String[] args) { Map<String, Object> defaultProperties = new HashMap<>(); defaultProperties.put("server.port", "9000"); // 自定義默認端口 SpringApplication app = new SpringApplication(SpringBootBaseApplication.class); app.setDefaultProperties(defaultProperties); app.run(args); } }
@PropertySource注解
@PropertySource
注解用于在Spring Boot的@Configuration
類上加載外部屬性文件。當(dāng)我們在配置類上使用@PropertySource
時,需要注意的是,這些屬性源并不會立即被添加到Spring的Environment
中。它們是在Spring應(yīng)用上下文刷新(refresh)階段才會被真正加載并合并到環(huán)境變量中。
有興趣的可以跟一下源碼,org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors中執(zhí)行的。
Spring Boot的主引導(dǎo)配置,如服務(wù)器端口(server.port)、日志框架的初始化(例如日志級別設(shè)置)等,也是在應(yīng)用上下文刷新之前就被讀取并應(yīng)用的。因此,對于這類早期就需要讀取的配置,應(yīng)該直接在application.properties
或者環(huán)境變量等更早被加載的配置源中進行設(shè)置。
我們創(chuàng)建一個propertysource.properties
文件:
server.port = 9001 coderacademy.name = CoderAcademy
然后我們在@Configuration
配置上使用@PropertySource
導(dǎo)入propertysource.properties
文件。
@PropertySource(value = "classpath:propertysource.properties") @Configuration public class MyConfig { }
我們在應(yīng)用啟動后看一下上述配置:
@SpringBootApplication public class SpringBootBaseApplication { public static void main(String[] args) { Map<String, Object> defaultProperties = new HashMap<>(); defaultProperties.put("server.port", "9000"); // 自定義默認端口 SpringApplication app = new SpringApplication(SpringBootBaseApplication.class); app.setDefaultProperties(defaultProperties); ConfigurableApplicationContext context = app.run(args); Environment environment = context.getEnvironment(); System.out.println("coderacademy.name: " + environment.getProperty("coderacademy.name")); } }
打印結(jié)果:
可以看出server.port
變成了9001,即@PropertySource
加載的配置覆蓋了SpringBoot
默認的屬性值。
Config Data(配置數(shù)據(jù))
Config Data(配置數(shù)據(jù))是Spring Boot中用于外部化應(yīng)用配置的核心部分。主要由內(nèi)部配置文件以及外部配置文件。
內(nèi)部配置文件
內(nèi)部配置文件最基礎(chǔ)的應(yīng)用配置文件,位于項目構(gòu)建后的jar包內(nèi)部。位于src/main/resource
目錄下的文件。
外部配置文件
可以將配置文件放在jar包外面的某個路徑下。這種方式有助于在不修改jar包的情況下變更配置。比如我們使用的配置中心(nacos
,apollo
等),也可以通過spring.config.location
或者spring.config.additional-location
指定的文件等。
SpringBoot在啟動時會默認從特定的目錄中加載這些配置文件。我們可以從ConfigDataEnvironment
中找到這些目錄:
其目錄的加載順序由低到高為:
file:./ file:./config/ file:./config/*/ classpath:/ classpath:/config/
其中file
代表應(yīng)用根目錄下的文件,而classpath
為resources
下的文件。
這些配置文件的配置優(yōu)先級順序由低到高為:
classpath:/ classpath:/config/ file:./ file:./config/ file:./config/*/
本例基于SpringBoot2.7版本。
我們分別在這些目錄下創(chuàng)建配置文件application.properties
:
我們在對應(yīng)文件中寫入他們的目錄路徑:
1: config.data.path = classpath:./ 2: config.data.path = classpath:./config/ 3: config.data.path = file:./ 4: config.data.path = file:./config/ 5: config.data.path = file:./config/dev
我們在SpringBoot啟動時打印config.data.path
的值:
@SpringBootApplication public class SpringBootConfigApplication { public static void main(String[] args) { Map<String, Object> defaultProperties = new HashMap<>(); defaultProperties.put("server.port", "9000"); // 自定義默認端口 SpringApplication app = new SpringApplication(SpringBootConfigApplication.class); app.setDefaultProperties(defaultProperties); ConfigurableApplicationContext context = app.run(args); Environment environment = context.getEnvironment(); System.out.println("config.data.path: " + environment.getProperty("config.data.path")); } }
我們分步進行驗證,先驗證1,2,打印結(jié)果:
config.data.path: classpath:./config/
繼續(xù)驗證1,2,3,打印結(jié)果:
config.data.path: file:./
驗證1,2,3,4,打印結(jié)果:
config.data.path: file:./config/
驗證1,2,3,4,5,打印結(jié)果:
config.data.path: file:./config/dev
隨機值屬性源
RandomValuePropertySource 在Spring Boot中,RandomValuePropertySource
是一個特殊屬性源,它并不來源于固定的配置文件或環(huán)境變量,而是由Spring Boot框架在啟動時自動添加。這個屬性源提供的屬性名以random.*
開頭,可以用于生成隨機值。例如,你可以在配置文件中引用random.int
或random.long
等屬性,Spring Boot在啟動時會為這些屬性生成隨機整數(shù)值。這對于需要在運行時生成一些臨時或隨機值的場景非常有用,如臨時密碼、緩存密鑰等。
比如我們在application.properties
中設(shè)置random.int=100
random.int=100
我們在SpringBoot啟動時獲取``random.int`的值:
@SpringBootApplication public class ConfigApplication { public static void main( String[] args ) { SpringApplication app = new SpringApplication(ConfigApplication.class); ConfigurableApplicationContext context = app.run(args); Environment environment = context.getEnvironment(); System.out.println("random.int: " + environment.getProperty("random.int")); } }
打印結(jié)果為:
random.int: -510589238
并且每次重新啟動應(yīng)用,打印的結(jié)果都不一樣。
操作系統(tǒng)環(huán)境變量
在Spring Boot中,環(huán)境變量可以用作配置源,Spring Boot會自動檢測并加載這些環(huán)境變量作為應(yīng)用的配置屬性。例如,如果在操作系統(tǒng)中設(shè)置了環(huán)境變量MY_APP_PORT=8080
,那么在Spring Boot應(yīng)用中可以通過${MY_APP_PORT}
來引用這個值。
我們設(shè)置環(huán)境變量為config.data.path=環(huán)境變量
:
我們啟動引用,依然打印config.data.path
的結(jié)果為:
config.data.path: 環(huán)境變量
Java系統(tǒng)屬性
Java系統(tǒng)屬性是通過System.setProperty()
方法設(shè)置一系列鍵值對。
@SpringBootApplication public class ConfigApplication { static { System.setProperty("config.data.path", "SystemProperty"); // 設(shè)置系統(tǒng)屬性 } public static void main( String[] args ) { SpringApplication app = new SpringApplication(ConfigApplication.class); ConfigurableApplicationContext context = app.run(args); Environment environment = context.getEnvironment(); System.out.println("config.data.path: " + environment.getProperty("config.data.path")); } }
打印結(jié)果為:
config.data.path: SystemProperty
SPRING_APPLICATION_JSON環(huán)境變量中的內(nèi)嵌JSON屬性
SPRING_APPLICATION_JSON
是 Spring Boot 提供的一種機制,允許通過環(huán)境變量傳遞 JSON 格式的配置給應(yīng)用程序。這個環(huán)境變量的內(nèi)容會被解析成一個 JSON 對象,并合并到Spring的Environment
中,就像其他屬性源一樣。
@SpringBootApplication public class ConfigApplication { static { System.setProperty("config.data.path", "SystemProperty"); // 設(shè)置系統(tǒng)屬性 System.setProperty("SPRING_APPLICATION_JSON", "{\"config.data.path\":\"SPRING_APPLICATION_JSON環(huán)境變量中的內(nèi)嵌JSON屬性\"}"); } public static void main( String[] args ) { SpringApplication app = new SpringApplication(ConfigApplication.class); ConfigurableApplicationContext context = app.run(args); Environment environment = context.getEnvironment(); System.out.println("config.data.path: " + environment.getProperty("config.data.path")); } }
打印結(jié)果:
config.data.path: SPRING_APPLICATION_JSON環(huán)境變量中的內(nèi)嵌JSON屬性
命令行參數(shù)
啟動Spring Boot應(yīng)用時,可以直接通過命令行參數(shù)來覆蓋或設(shè)置配置屬性。命令行參數(shù)通常以--
開頭,后面緊跟屬性名和值,如--server.port=8080
。這種方式可以在不修改配置文件的前提下臨時調(diào)整應(yīng)用配置。命令行參數(shù)具有較高的優(yōu)先級,可以覆蓋其它配置源中的屬性值。
我們使用java -jar
啟動SpringBoot:
java -jar ./springboot-config-1.0-SNAPSHOT.jar --config.data.path=命令行參數(shù)
打印結(jié)果為:
config.data.path: 命令行參數(shù)
關(guān)于SpringBoot的jar包,可以通過java -jar命令直接執(zhí)行的原因請參考:字節(jié)二面:為什么SpringBoot的 jar 可以直接運行?我說內(nèi)嵌了Tomcat容器,他讓我出門左轉(zhuǎn)
至于其他的跟單測有關(guān)的配置,我們就不一一細說了
總結(jié)
Spring Boot配置加載優(yōu)先級的設(shè)計具有深遠的實際意義和重要性。這一機制確保了應(yīng)用在不同環(huán)境和部署場景下的高度靈活性和可移植性,同時也極大提升了開發(fā)和運維團隊的生產(chǎn)力和協(xié)同效率。
優(yōu)先級順序的嚴謹性使得開發(fā)者能夠精細地控制配置的覆蓋層級,從而使同一份代碼可以根據(jù)不同環(huán)境的需求加載不同的配置屬性。例如,在開發(fā)、測試和生產(chǎn)環(huán)境中分別啟用不同的數(shù)據(jù)庫連接、日志級別或API密鑰等敏感信息,而無需在代碼中硬編碼。
Spring Boot的配置加載流程首先考慮了默認配置,然后逐步加載用戶通過@PropertySource
注解引入的屬性源、打包在jar包內(nèi)外的各種application.properties和application-{profile}.properties或YAML文件、環(huán)境變量、系統(tǒng)屬性,直至命令行參數(shù)等。這種分層加載策略確保了越靠后的配置源擁有更高的優(yōu)先級,從而可以覆蓋之前的配置,這也體現(xiàn)了配置的靈活性和即時性。
理解并合理運用Spring Boot配置加載的優(yōu)先級,對于保障應(yīng)用的安全性、可維護性以及降低部署復(fù)雜度至關(guān)重要。特別是在大規(guī)模微服務(wù)架構(gòu)中,合理的配置管理和遷移對于整體系統(tǒng)的穩(wěn)定性有著不可忽視的作用。通過對配置優(yōu)先級的深入掌控,開發(fā)者和運維人員能夠輕松應(yīng)對復(fù)雜環(huán)境下的配置管理挑戰(zhàn),使得Spring Boot應(yīng)用具備良好的擴展性和適應(yīng)性。
以上就是SpringBoot讀取配置優(yōu)先級順序的方法詳解的詳細內(nèi)容,更多關(guān)于SpringBoot優(yōu)先級順序的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳細分析Java中String、StringBuffer、StringBuilder類的性能
在Java中,String類和StringBuffer類以及StringBuilder類都能用于創(chuàng)建字符串對象,而在分別操作這些對象時我們會發(fā)現(xiàn)JVM執(zhí)行它們的性能并不相同,下面我們就來詳細分析Java中String、StringBuffer、StringBuilder類的性能2016-05-05一文詳細解析Java?8?Stream?API中的flatMap方法
這篇文章主要介紹了Java?8?Stream?API中的flatMap方法的相關(guān)資料,flatMap方法是Java?StreamAPI中的重要中間操作,用于將流中的每個元素轉(zhuǎn)換為一個新的流,并將多個流合并為一個單一的流,常用于處理嵌套集合和一對多映射,需要的朋友可以參考下2024-12-12SpringBoot整合Freemarker實現(xiàn)頁面靜態(tài)化的詳細步驟
這篇文章主要介紹了SpringBoot整合Freemarker實現(xiàn)頁面靜態(tài)化,第一步要創(chuàng)建項目添加依賴,本文分步驟給大家詳細講解,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10