Spring Boot從零入門7_最新配置文件配置及優先級詳細介紹

本文屬於原創,轉載註明出處,歡迎關注微信小程序小白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-devtoolsjar包,提供了一些方便程序開發的功能,主要是監控程序的變化,然後進行自動重新啓動。使用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對參數的重寫(覆蓋)有一個順序,這是我們需要注意的,這裏概況如下:

  1. 當使用了Devtools時,$HOME/.config/spring-boot文件夾中的Devtools全局設置屬性
  2. @TestPropertySource針對對測試的註解
  3. 測試的properties。在@SpringBootTest和測試註釋中提供,用於測試應用程序的特定部分
  4. 命令行參數
  5. 來自SPRING_APPLICATION_JSON(內嵌在環境變量或系統屬性中的JSON)的屬性
  6. ServletConfig初始化參數
  7. ServletContext初始化參數
  8. JNDI屬性:java:comp/env
  9. Java系統屬性: System.getProperties()
  10. 操作系統環境變量
  11. RandomValuePropertySource,其屬性僅在random.*
  12. 打包jar之外的特定於概要文件的應用程序屬性(如application-{profile}.properties和對應的YAML變量)
  13. 打包在jar中的特定於概要文件的應用程序屬性(如application-{profile}.properties和YAML變量)
  14. 打包jar之外的應用程序屬性(application.properties和YAML變量)
  15. 打包在jar中的應用程序屬性(application.properties和YAML變量)
  16. @Configuration類上的@PropertySource註解
  17. 默認屬性(通過設置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,一個是ymlSpringApplicationapplication.properties文件加載以下位置的屬性,並將它們添加到Spring環境中。

  1. 當前項目目錄的config子目錄
  2. 當前項目根目錄
  3. classpath設定目錄下的config子目錄
  4. 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-parentPOM,則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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章