本文屬於原創,轉載註明出處,歡迎關注微信小程序小白AI博客
微信公衆號小白AI
或者網站 https://xiaobaiai.net 或者我的CSDN http://blog.csdn.net/freeape
文章目錄
0 前言
進入實際項目開發中,我們不僅僅是靠着默認的全局配置文件application.properties
來配置我們的項目了,Spring Boot中的配置文件也有不少需要注意的地方,掌握後,可以方便的讓我們在做項目中游刃於各種配置了,讓配置一目瞭然,層次清楚,模塊清晰,寫成總結,另外方便以後參考查閱。該文章的內容是最新的配置文件介紹內容,全部參考最新的官方文檔所寫,截至2019年11月21日,此時Spring是5.2.1
版本,Spring Boot是2.2.1
版本。
1 我們需要了解
Spring Boot
提供了一個spring-boot-devtools
jar包,提供了一些方便程序開發的功能,主要是監控程序的變化,然後進行自動重新啓動。使用spring-boot-devtools
需要在pom.xml
中添加依賴項,同時需要設置<optional>true</optional>
。spring-boot-devtools
默認將只在開發環境
生效,通過Spring Boot
插件打包時默認是不會包含spring-boot-devtools
。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
2 簡介和全局概述
創建一個Spring Boot
項目後,會在src/main/resources
目錄下默認生成一個全局配置文件application.properties
,不過裏面啥內容也沒有。Spring Boot已經將所有默認配置參數都自動配置好了(https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html),如果我們在外部配置文件中修改配置,則默認配置參數就會被修改(Externalized Configuration,配置外部化),配置外部化的方式有好幾種,可以使用屬性文件(properties file)、YAML文件、環境變量和命令行參數將配置外部化,下面內容會詳細介紹。
比如配置文件可以是application.properties
或者是application.yml
,都可以配置,只是裏面的配置語法不一樣而已,yml格式層次相比properties格式要分明些。
.properties
寫法:
server.port = 9090
spring.application.name = demoservice
.yml
寫法:
spring:
application:
name: demoservice
server:
port: 9090
注意:yml格式中一定不要用製表符tab,冒號和值之間一定要有空格。
Spring Boot對參數的重寫(覆蓋)有一個順序,這是我們需要注意的,這裏概況如下:
- 當使用了
Devtools
時,$HOME/.config/spring-boot
文件夾中的Devtools全局設置屬性 @TestPropertySource
針對對測試的註解- 測試的
properties
。在@SpringBootTest
和測試註釋中提供,用於測試應用程序的特定部分 - 命令行參數
- 來自
SPRING_APPLICATION_JSON
(內嵌在環境變量或系統屬性中的JSON)的屬性 ServletConfig
初始化參數ServletContext
初始化參數- JNDI屬性:
java:comp/env
- Java系統屬性:
System.getProperties()
- 操作系統環境變量
RandomValuePropertySource
,其屬性僅在random.*
中- 打包
jar之外
的特定於概要文件的應用程序屬性(如application-{profile}.properties
和對應的YAML變量) - 打包
在jar中
的特定於概要文件的應用程序屬性(如application-{profile}.properties
和YAML變量) - 打包
jar之外
的應用程序屬性(application.properties
和YAML變量) - 打包
在jar中
的應用程序屬性(application.properties
和YAML變量) @Configuration
類上的@PropertySource
註解- 默認屬性(通過設置
SpringApplication.setDefaultProperties
指定)
舉一個具體的例子來說明上述的順序是如何生效的:
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
比如在應用程序類路徑(例如,打包在jar內
)上,可以有一個application.properties
文件,該文件爲name
屬性設置了默認屬性值。在新環境中運行時,可以在jar外部
提供application.properties
文件,該文件覆蓋會覆蓋在jar內
的application.properties
。又如對於一次性測試,可以使用特定的命令行開關啓動(例如,java -jar app.jar --name="Spring"
)也可以覆蓋name
屬性值。又如可以JSON格式環境變量$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
來覆蓋。其他方式就不一一舉例了。
3 配置參數
3.1 參數設置及加載目錄的順序及作用優先級
參數設置有兩種方式語法,一個properties
,一個是yml
,SpringApplication
從application.properties
文件加載以下位置的屬性,並將它們添加到Spring環境
中。
- 當前項目目錄的
config
子目錄 - 當前項目根目錄
- classpath設定目錄下的
config
子目錄 - classpath設定目錄下
上述列表按優先級排序(在列表中較高位置定義的屬性將覆蓋在較低位置定義的屬性,如1中設置的屬性值將覆蓋2中同屬性的屬性值)。
注意:用maven構建項目時,src/main/resources目錄就是默認的classpath
另外這裏說下yml的注意點和特殊用法。
- yml格式中一定不要用製表符tab,冒號和值之間
一定要有空格
一定要有空格
一定要有空格
- yml的雙引號不會轉義字符串裏面的特殊字符,特殊字符按本身功能輸出,比如
- yml的單引號會轉義字符串裏面的特殊字符,輸出原來的字符
# 下面會輸出得到hello換行xiaobaiai.net
name: "hello\nxiaobaiai.net"
# 下面會輸出得到hello\nxiaobaiai.net
name: 'hello\nxiaobaiai.net'
- yml支持對象、數組、純量(字符串、布爾值true/false、整數、浮點數、null、時間、日期
new Date('1976-07-31')
)
# 對象行內寫法
students: { name: Steve, age: 22 }
# 數組行內寫法
animal: [Cat, Dog]
# 或者數組非行內寫法
animal:
- Cat
- Dog
3.2 生成配置參數隨機值
生成配置參數隨機值在測試或者某些場景下是非常有用的。
如我們配置:
#random int
app.location-x=${random.int}
app.location-y=${random.int}
#random int with max
app.user-age=${random.int(100)}
#random int range
app.max-users=${random.int[1,10000]}
#random long with max
app.refresh-rate-milli=${random.long(1000000)}
#random long range
app.initial-delay-milli=${random.long[100,90000000000000000]}
#random 32 bytes
app.user-password=${random.value}
#random uuid. Uses java.util.UUID.randomUUID()
app.instance-id=${random.uuid}
最後輸出:locationX=-449689812, locationY=-2048116572, userAge=88, maxUsers=8927, refreshRateMilli=418859, initialDelayMilli=12542150790115755, userPassword=95ea8b43fd16dc26aad0030c1340e723, instanceId=bd252902-54e9-47b3-bebf-a81b1300ff69
3.3 參數間引用(佔位符)
在配置參數中可以通過佔位符來實現引用之前定義的參數值,如:
app.name=MyApp
app.description=${app.name} is a Spring Boot application
有些人喜歡(例如)使用--port=9000
而不是--server.port=9000
在命令行上設置配置屬性。可以通過在application.properties
中使用佔位符
來啓用此行爲:
server.port=${port:8080}
注意:如果繼承自
spring-boot-starter-parent
POM,則maven資源插件的默認篩選標記已從${*}
更改爲@
(即,@maven.token@
而不是${maven.token}
),以防止與spring樣式佔位符衝突。如果直接爲application.properties
啓用了Maven篩選,則可能還需要將默認篩選標記更改爲其他分隔符,而不是@
。
3.4 自定義配置文件
3.4.1 方式一
如果不喜歡將application.properties
作爲配置文件名,可以通過指定spring.config.name
環境屬性切換到另一個文件名。還可以使用spring.config.location
環境屬性(目錄位置或文件路徑的逗號分隔列表)指定配置文件位置。以下示例演示如何指定其他文件名:
$ java -jar myproject.jar --spring.config.name=myConfig
下面的示例演示如何指定兩個位置:
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
如果spring.config.location
包含目錄(而不是文件),則需要以/
結尾,並且,運行的時候,在加載配置之前,應該附加從spring.config.name
配置的名稱或者默認配置名稱。默認不配置spring.config.location
則搜索配置文件順序爲:
file:./config/
file:./
classpath:/config/
classpath:/
使用時配置自定義配置位置時spring.config.location
,它們會替換默認位置。例如,如果spring.config.location
配置了值classpath:/custom-config/,file:./custom-config/
,則搜索順序將變爲:
file:./custom-config/
classpath:custom-config/
當使用配置自定義配置位置時spring.config.additional-location
,除了額外配置路徑外,還會使用默認位置。在默認位置之前搜索其他位置。
注意:在編程環境中,直接去application.properties中設置spring.config.name是無法生效的,只有在命令行或者設置環境變量
export SPRING_CONFIG_NAME=myConfig
,或則在代碼中去手動編碼導入指定路徑中的配置文件。
3.4.2 方式二
直接通過編碼加載指定配置文件去實現,這種方式其實跟後面小節講到的自定義屬性是相通的,只多了一個指定文件名的註解,更詳細的可以看後面操作。比如我們創建test.properties
,路徑跟也放在src/main/resources
下面:
my.app.name=hello
my.app.func=test
然後新建一個參數Bean:
@Configuration
@ConfigurationProperties(prefix="my.app")
@PropertySource("classpath:test.properties")
public class ConfigTestBean {
private String name;
private String func;
// 省略getter和setter
}
這樣就Ok了,怎麼是驗證可以看自定義配置參數小節。
3.5 命令行配置參數
默認情況下,SpringApplication
將任何命令行選項參數(即以--
開頭的參數,例如--server.port=9000
)轉換爲屬性,並將它們添加到Spring環境
中。如前所述,命令行屬性順序排在第四,始終優先於其下面屬性源。
如果不希望命令行屬性添加到Spring環境
中,可以在程序中使用SpringApplication.setAddCommandLineProperties(false)
禁用它們。
3.6 特定於配置文件的屬性(激活profile)
除了application.properties
文件外,還可以使用以下命名約定定義特定於配置文件的屬性:application-{profile}.properties
。環境有一組默認配置文件(默認情況下profile爲default,即application-default.properties),如果未設置活動配置文件,則使用默認的application-default.properties
文件,加載順序和優先級還是與application.properties
一樣的。Spring
可使用Profile決定程序在不同環境下執行情況,包含配置、加載Bean、依賴等,Spring
的Profile一般項目包含:dev(開發), test(單元測試), qa(集成測試), prod(生產環境)。同樣地,Maven也有Profile配置,可在構建過程中針對不同的Profile環境執行不同的操作,包含配置、依賴、行爲等,每個Profile中可設置:id(唯一標識), properties(配置屬性), activation(自動觸發的邏輯條件), dependencies(依賴)等。
說到這裏,如何激活profile呢?下面介紹三種方式。
注意:application-{profile}.properties的重寫優先級是要高於application.properties的,這個跟配置文件
加載順序
沒有關係。另外創建application-{profile}.yml文件跟properties是一樣的。
3.6.1 方式一
在配置文件中設置,這種方式不靈活,實際開發中不不太會用到
spring.profiles.active=test
3.6.2 方式二
使用佔位符,在打包時替換,以Maven爲例
第一步在properties中添加(package.target是自定義的參數):
[email protected]@
第二步在pom.xml中增加不同環境打包的配置:
<!-- 與Maven build標記並列 -->
<profiles>
<!-- 開發環境 -->
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<package.target>dev</package.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
</dependencies>
</profile>
<!-- 生產環境 -->
<profile>
<id>prod</id>
<properties>
<package.target>prod</package.target>
</properties>
</profile>
</profiles>
從上面的配置可以看出,Maven的Profile配置了兩個:dev和prod,並且在dev中使用了內嵌Tomcat,而 prod 中沒有(這種配置場景如生產環境下使用外部Tomcat,開發時使用內部Tomcat)。
第三步再添加資源過濾實現在構建時修改以“@xxx@”表示的屬性,利用Maven的resources插件:
...
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- Maven build標記內 -->
<resources>
<!-- 不加該resource也可以 -->
<resource>
<directory>src/main/resources</directory>
<excludes>
<!-- 排除掉src/main/resources下的所有application*.properties文件 -->
<exclude>application*.properties</exclude>
</excludes>
</resource>
<resource>
<!-- 指定要處理的資源目錄 -->
<directory>src/main/resources</directory>
<!-- 是否替換@xx@表示的maven properties屬性值 -->
<filtering>true</filtering>
<includes>
<!-- 將文件內容的“@xx@”替換爲相應的變量,即package.target -->
<include>application-${package.target}.properties</include>
</includes>
</resource>
</resources>
第四步就是編譯打包了:
# 根據Maven Profile的 dev 構建環境包
$ mvn clean package -Dmaven.test.skip=true -Pdev
3.6.3 方式三
通過設置系統環境變量:
export SPRING_PROFILES_ACTIVE=dev
3.6.4 其他方式
Java命令行設定方式:
# 方式一
$ java -jar app.jar --spring.profiles.active=dev
# 方式二
$ java -jar -Dspring.profiles.active=dev demo-0.0.1-SNAPSHOT.jar
註解方式(@ActiveProfiles
是Spring Boot的Test starter提供的註解,在單元測試中就比較有用了,只能在/src/test/java
中使用):
@ActiveProfiles("dev")
3.6.5 YML特殊方式
YAML文件實際上可以是由---
行分隔的一系列文檔,每個文檔被分別解析爲一個展開的配置映射。然後激活dev還是production的方式同樣的也有指定環境變量或者命令行方式之類的。
# 通用屬性
server:
port: 9000
---
# dev環境下配置
spring:
profiles: dev
server:
port: 9001
---
# production生產環境下配置
spring:
profiles: production
server:
port: 0
3.7 自定義屬性
Spring
已經爲我們提供很多的默認參數,不過我們也可以創建自己的配置參數。比如我們在application.properties
中創建下面的自定義屬性:
#random int
app.location-x=${random.int}
app.location-y=${random.int}
#random int with max
app.user-age=${random.int(100)}
#random int range
app.max-users=${random.int[1,10000]}
#random long with max
app.refresh-rate-milli=${random.long(1000000)}
#random long range
app.initial-delay-milli=${random.long[100,90000000000000000]}
#random 32 bytes
app.user-password=${random.value}
#random uuid. Uses java.util.UUID.randomUUID()
app.instance-id=${random.uuid}
然後創建對應的Java參數組件MyAppProperties.java
:
@Component
@ConfigurationProperties("app")
public class MyAppProperties {
private int locationX;
private int locationY;
private int userAge;
private int maxUsers;
private long refreshRateMilli;
private long initialDelayMilli;
private String userPassword;
private UUID instanceId;
public int getLocationX() {
return locationX;
}
public void setLocationX(int locationX) {
this.locationX = locationX;
}
public int getLocationY() {
return locationY;
}
public void setLocationY(int locationY) {
this.locationY = locationY;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
public int getMaxUsers() {
return maxUsers;
}
public void setMaxUsers(int maxUsers) {
this.maxUsers = maxUsers;
}
public long getRefreshRateMilli() {
return refreshRateMilli;
}
public void setRefreshRateMilli(long refreshRateMilli) {
this.refreshRateMilli = refreshRateMilli;
}
public long getInitialDelayMilli() {
return initialDelayMilli;
}
public void setInitialDelayMilli(long initialDelayMilli) {
this.initialDelayMilli = initialDelayMilli;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public UUID getInstanceId() {
return instanceId;
}
public void setInstanceId(UUID instanceId) {
this.instanceId = instanceId;
}
@Override
public String toString() {
return "MyAppProperties [locationX=" + locationX + ", locationY=" + locationY + ", userAge=" + userAge
+ ", maxUsers=" + maxUsers + ", refreshRateMilli=" + refreshRateMilli + ", initialDelayMilli="
+ initialDelayMilli + ", userPassword=" + userPassword + ", instanceId=" + instanceId + "]";
}
}
@ConfigurationProperties
註解向Spring Boot
聲明該類中的所有屬性和配置文件中相關的配置進行綁定。prefix = "app"
(prefix=可省略
) : 聲明配置前綴,將該前綴下的所有屬性進行映射。@Component
或者@Configuration
:將該組件加入Spring Boot
容器,只有這個組件是容器中的組件,配置才生效。
提示:也可以通過
@Value("${key}")
讀取配置文件中的屬性,key = properties
文件等號左邊的key部分。在我們定義的 Java 參數組件中,還可以對具體的參數進行註解斷言,如@Email加到郵件的變量上,則如果注入的不是一個合法的郵件地址則會拋出異常。這類註解還有@AssertFalse
校驗false、@AssertTrue
校驗true、@DecimalMax(value=10,inclusive=true)
小於等於10,inclusive=true,是小於等於、@DecimalMin(value=,inclusive=)
、@Max(value=)
小於等於value、@Min(value=)
大於等於value、@Past
檢查日期、@Pattern(regex=,flag=)
正則、@Validate
對po實體類進行校驗等。
最後還需要加入依賴spring-boot-configuration-processor
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 加入spring-boot-configuration-processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
校驗我們自定義的配置:
@SpringBootApplication
public class Test07HelloworldApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Test07HelloworldApplication.class, args);
MyAppProperties bean = context.getBean(MyAppProperties.class);
System.out.println(bean.toString());
}
}
// output
// MyAppProperties [locationX=1329054364, locationY=1100464591, userAge=99, maxUsers=8007, refreshRateMilli=733281, initialDelayMilli=54489880705550952, userPassword=76aebd15270f7065adf3d31b5a790829, instanceId=681ed3a4-a561-460c-b826-58229c31b055]
4 總結
上述的內容大部分經過實際Demo驗證,示例代碼可以在https://github.com/yicm/SpringBootExamples上找到。配置文件這一章細節內容比較多,但是我們把我幾個點就好了,這個總結下:
- Spring Boot爲我們提供了大量的默認配置,我們可以重寫這些配置參數的值,並提供了多種方式去重寫(覆蓋),且重寫方式之間是有優先級的
- Spring Boot應用可以在不同的位置加載配置文件
application.properties(yml)
,並且這些位置是有順序、優先級的 - Spring Boot的參數之間可以通過佔位符引用,而且還可以通過佔位符實現命令行參數名字的簡化
- Spring Boot可以支持自定義參數
- Spring Boot可以支持自定義配置文件名
- Spring Boot可以支持多配置文件的切換,通過
application-{profile}.properties(yml)
激活profile,且有多種方式去激活profile
如果你知道了上述總結的具體內容,那麼這一博文你也差不多都瞭解了。
5 參考資料
- https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
- https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-yaml
- https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
- https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/api//org/springframework/boot/SpringApplication.html
- https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/test/context/TestPropertySource.html
- https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-autoconfigured-tests
- http://www.ruanyifeng.com/blog/2016/07/yaml.html
- https://yaml.org/spec/1.2/spec.html
- https://segmentfault.com/a/1190000011770028