從零開始學(xué)Spring Boot系列-外部化配置
Spring Boot 允許你將配置外部化,以便可以在不同的環(huán)境中使用相同的應(yīng)用程序代碼。可以使用屬性文件、YAML文件、環(huán)境變量和命令行參數(shù)將配置外部化。屬性值可以通過使用 @Value 注解直接注入 bean,可以通過 Spring 的 Environment 抽象訪問,也可以通過 @ConfigurationProperties。
Spring Boot 使用一種非常特殊的 PropertySource 順序,其設(shè)計(jì)目的是允許合理地覆蓋值。屬性按以下順序考慮:
(1)主目錄上的 Devtools 全局設(shè)置屬性(Devtools 處于活動狀態(tài)時(shí) ~/.spring-boot-devtools.properties)。
(2)測試中的 @TestPropertySource 注解。
(3)測試中的 properties 屬性。在 @SpringBootTest 和測試注解中提供,用于測試應(yīng)用程序的特定部分。
(4)命令行參數(shù)。
(5)來自 SPRING_APPLICATION_JSON(內(nèi)嵌在環(huán)境變量或系統(tǒng)屬性中的 JSON)的屬性。
(6)ServletConfig 初始化參數(shù)。
(7)ServletContext 初始化參數(shù)。
(8)來自 java:comp/env 的 JNDI 屬性。
(9)Java 系統(tǒng)屬性(System.getProperties())。
(10)OS(操作系統(tǒng))環(huán)境變量。
(11)僅在 random.* 中具有屬性的 RandomValuePropertySource。
(12)打包的 jar 之外的特定配置的應(yīng)用程序?qū)傩裕╝pplication-{profile}.properties 和 YAML 變體)。
(13)打包到 jar 內(nèi)的特定配置的應(yīng)用程序?qū)傩裕╝pplication-{profile}.properties 和 YAML 變體)。
(14)打包的 jar 之外的應(yīng)用程序?qū)傩裕╝pplication.properties 和 YAML 變體)。
(15)打包到 jar 內(nèi)的應(yīng)用程序?qū)傩裕╝pplication.properties 和 YAML 變體)。
(16)@Configuration 類上的 @PropertySource 注解。
(17)默認(rèn)屬性(通過設(shè)置 SpringApplication.setDefaultProperties 指定)。
為了提供一個(gè)具體的示例,假設(shè)您開發(fā)了一個(gè)使用 name 屬性的 @Component,如下面的示例所示:
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
在應(yīng)用程序類路徑上(例如,在 jar 中)可以有一個(gè) application.properties 文件,為 name 提供一個(gè)合理的默認(rèn)屬性值。在新環(huán)境中運(yùn)行時(shí),可以在 jar 外部提供 application.properties 文件,該文件覆蓋 name。對于一次性測試,可以使用特定的命令行開關(guān)啟動(例如,java -jar app.jar --name="Spring")。
提示:SPRING_APPLICATION_JSON 屬性可以在帶有環(huán)境變量的命令行上提供。例如,可以在 UN*X shell 中使用以下行:
SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
在前面的示例中,你在 Spring Environment 中使用 acme.name=test 結(jié)尾。你還可以在系統(tǒng)屬性中將 JSON 作為 spring.application.json 提供。
$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
還可以使用命令行參數(shù)提供 JSON,如下面的示例所示:
$ java -jar myapp.jar --spring.application.json='{"name":"test"}'
還可以將 JSON 作為 JNDI 變量提供,如下所示:java:comp/env/spring.application.json 。
配置隨機(jī)值
RandomValuePropertySource 用于注入隨機(jī)值(例如,在機(jī)密或測試用例中)。它可以產(chǎn)生 integers、longs、uuids 或字符串,如下面示例所示:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
random.int* 語法是:OPEN value(,max) CLOSE,其中 OPEN、CLOSE 是任何字符,value、max 是整數(shù)。如果提供 max,那么 value 是最小值,max 是最大值(不包含 max)。
訪問命令行屬性
默認(rèn)情況下,SpringApplication 將任何命令行選項(xiàng)參數(shù)(即以 -- 開頭的參數(shù),例如:--server.port=9000)轉(zhuǎn)換為一個(gè) property,并將它們添加到 Spring Environment。如前所述,命令行屬性始終優(yōu)先于其他屬性源。
如果不希望命令行屬性添加到 Enviroment,則可以使用 SpringApplication.setAddCommandLineProperties(false) 禁用它們。
特定配置的屬性
除了 application.properties 文件外,還可以使用以下命名約定定義特定配置的屬性:application-{profile}.properties。Environment 有一組默認(rèn)配置(默認(rèn)情況下是 [default]),如果未設(shè)置活動配置,則使用這些配置。換句話說,如果沒有顯示激活配置文件,則加載 application-default.properties 中的屬性。
特定配置的屬性是從與標(biāo)準(zhǔn) application.properties 相同的位置加載的。不論特定配置的文件是在打包的 jar 內(nèi)部還是外部,特定配置的文件始終覆蓋非特定的文件。
如果指定了多個(gè)配置文件,則應(yīng)用“最后勝出”策略。例如,spring.profiles.active 屬性指定的配置文件將添加到通過 SpringApplication API 配置的文件之后,因此具有優(yōu)先權(quán)。
注釋:如果在 spring.config.location 中指定了任何文件,則不考慮這些文件的特定配置的變體。如果還想使用特定配置的屬性,請?jiān)?spring.config.location 中使用目錄。
屬性中的占位符
application.properties 中的值在使用時(shí)通過現(xiàn)有 Environment 進(jìn)行篩選,因此可以引用以前定義的值(例如,來自系統(tǒng)屬性的)。
app.name=MyApp
app.description=${app.name} is a Spring Boot application
提示:你還可以使用此技術(shù)創(chuàng)建現(xiàn)有 Spring Boot 屬性的“短”變體。
使用 YAML 代替 Properties
YAML 是 JSON 的超集,因此,它是一種用于指定分層配置數(shù)據(jù)的便捷格式。只要類路徑上有 Snake YAML 庫,SpringApplication 類就會自動支持 YAML 作為 properties 的替代者。
注釋:如果你使用“Starters”,Snake YAML 將由 spring.boot.starter 自動提供。
加載 YAML
Spring Framework 提供了兩個(gè)方便的類,可用于加載 YAML 文檔。YamlPropertiesFactoryBean 將 YAML 加載為 Properties,YamlMapFactoryBean 將 YAML 加載為 Map。
例如,考慮下面的 YAML 文檔:
environments:
dev:
url: https://dev.example.com
name: Developer Setup
prod:
url: https://another.example.com
name: My Cool App
前面的示例將轉(zhuǎn)換為以下屬性:
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
YAML 列表用帶有 [index] 取消引用(dereferencers)的屬性鍵表示。例如,考慮以下 YAML:
my:
servers:
- dev.example.com
- another.example.com
前面的示例將轉(zhuǎn)換為這些屬性:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
要通過使用 Spring Boot 的 Binder 工具類(這就是 @ConfigurationProperties 所做的)綁定到類似的屬性,需要在目標(biāo) bean 中有一個(gè) java.util.List(或 Set) 類型的屬性,并且需要提供 setter 或使用可變值初始化它。例如,下面的示例綁定到前面顯示的屬性:
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
在 Spring 環(huán)境中將 YAML 作為屬性公開
YamlPropertySourceLoader 類可用于在 Spring Environment 中將 YAML 公開為 PropertySource。這樣就可以使用帶有占位符語法的 @Value 注解來訪問 YAML 屬性。
多配置的 YAML 文檔
通過使用 spring.profiles 鍵指示文檔何時(shí)應(yīng)用,可以在單個(gè)文件中指定多個(gè)特定配置的 YAML 文檔,如下面示例所示:
server:
address: 192.168.1.100
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: production & eu-central
server:
address: 192.168.1.120
在上面的例子中,如果激活 development 配置,則 server.address 屬性是 127.0.0.1。類似地,如果激活 production 和 eu-central 配置,則 server.address 屬性是 192.168.1.120。如果未啟用 development、production 和 eu-central 配置,那么該屬性值是 192.168.1.100。
注釋:因此,spring.profiles 可以包含一個(gè)簡單的配置文件名(例如:production)或配置文件表達(dá)式。profile 表達(dá)式允許表達(dá)更復(fù)雜的 profile 邏輯,例如:production & (eu-central|eu-west)。
如果應(yīng)用程序上下文啟動時(shí)沒有顯示激活配置文件,則將激活默認(rèn)的。因此,在下面的 YAML 中,我們?yōu)?spring.security.user.password 設(shè)置一個(gè)值,它僅在“默認(rèn)”配置文件中可用:
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak
但是,在下面的示例中,始終設(shè)置密碼,因?yàn)樗鼪]有附加到任何配置文件,并且必須在所有其他配置文件中根據(jù)需要顯式重置密碼:
server:
port: 8000
spring:
security:
user:
password: weak
通過使用 spring.profiles 元素指定的 Spring 配置文件可以通過使用“!”字符取反。如果為單個(gè)文檔同時(shí)指定了否定配置文件和非否定配置文件,則必須至少有一個(gè)非否定配置文件匹配,并且不能有否定配置文件匹配。
YAML 的缺點(diǎn)
無法使用 @PropertySource 注解加載 YAML 文件。因此,如果需要以這種方式加載值,則需要使用屬性文件。
在特定配置的 YAML 文件中使用多個(gè) YAML 文檔語法可能會導(dǎo)致意外行為。例如,在名為 application-dev.yml 的文件中考慮以下配置,其中 dev 配置文件處于活動狀態(tài):
server:
port: 8000
---
spring:
profiles: !test
security:
user:
password: weak
在上面的例子中,profile 否定和 profile 表達(dá)式的行為將不符合預(yù)期。我們建議你不要將特定配置的 YAML 文件和多個(gè) YAML 文檔組合在一起,而只使用其中的一個(gè)。
類型安全的配置屬性
使用 @Value(${property}) 注解注入配置屬性有時(shí)會很麻煩,特別是在處理多個(gè)屬性或數(shù)據(jù)本身是分層的情況下。Spring Boot 提供了另一種處理屬性的方法,這種方法允許強(qiáng)類型 bean 控制和驗(yàn)證應(yīng)用程序的配置,如下面示例所示:
package com.example;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("acme")
public class AcmeProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { ... }
public void setEnabled(boolean enabled) { ... }
public InetAddress getRemoteAddress() { ... }
public void setRemoteAddress(InetAddress remoteAddress) { ... }
public Security getSecurity() { ... }
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() { ... }
public void setUsername(String username) { ... }
public String getPassword() { ... }
public void setPassword(String password) { ... }
public List<String> getRoles() { ... }
public void setRoles(List<String> roles) { ... }
}
}
上面的 POJO 定義了以下屬性:
(1)acme.enabled,默認(rèn)值為 false。
(2)acme.remote-address,其類型可以從 String 轉(zhuǎn)換過來。
(3)acme.security.username,具有嵌套的“security”對象,其名稱由屬性的名稱確定。特別是,返回類型根本沒有使用,它可能是 SecurityProperties。
(4)acme.security.password。
(5)acme.security.roles,包含一個(gè)字符串集合。
注釋:getter 和 setter 通常是必需的,因?yàn)榻壎ㄊ峭ㄟ^標(biāo)準(zhǔn)的 Java Beans 屬性描述符進(jìn)行的,就像 Spring MVC 一樣。在下列情況下,可省略 setter:
(1)Maps,只要它們被初始化,就需要一個(gè) getter,但不一定需要 setter,因?yàn)榻壎ㄆ骺梢詫λ鼈冞M(jìn)行修改。
(2)可以通過索引(通常使用 YAML)或使用單個(gè)逗號分隔值(屬性)訪問集合和數(shù)組。在后一種情況下,setter 是必需的。我們建議始終為這樣的類型添加 setter。如果初始化集合,請確保它不是不可變的(如前一個(gè)示例所示)。
(3)如果嵌套的 POJO 屬性被初始化(就像前面例子中 Security 字段),則 setter 不是必須的。如果希望綁定器使用實(shí)例的默認(rèn)構(gòu)造函數(shù)動態(tài)創(chuàng)建它,則需要一個(gè) setter。
有些人使用 Project Lombok 自動添加 getter 和 setter。確保 Lombok 不會為這樣的類型生成任何特定的構(gòu)造函數(shù),因?yàn)槿萜鲿詣邮褂盟鼇韺?shí)例化對象。
最后,只考慮標(biāo)準(zhǔn)的 Java Bean 屬性,不支持綁定靜態(tài)屬性。
你還需要在 @EnableConfigurationProperties 注解中列出要注冊的屬性類,如下面的示例所示:
@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
注釋:當(dāng) @ConfigurationProperties bean 以這種方式注冊時(shí),bean 有一個(gè)常規(guī)名稱:<prefix>-<fqn>,其中 <prefix> 是在 @ConfigurationProperties 注解中指定的環(huán)境鍵前綴,<fqn> 是 bean 的完全限定名。如果該注解沒有提供任何前綴,則只使用 bean 的全完限定名。
上面例子中的 bean 名稱是 acme-com.example.AcmeProperties。
前面的配置為 AcmeProperties 創(chuàng)建一個(gè)常規(guī) bean。我們建議 @ConfigurationProperties 只處理環(huán)境,特別是不要從上下文中注入其他 bean。請記住,@EnableConfigurationProperties 注解也會自動應(yīng)用到你的項(xiàng)目中,以便從 Environment 配置任何帶有 @ConfigurationProperties 注解的現(xiàn)有 bean。不是用 @EnableConfigurationProperties(AcmeProperties.class) 注解 MyConfiguration,你可以使 AcmeProperties 成為一個(gè) bean,如下面的示例所示:
@Component
@ConfigurationProperties(prefix="acme")
public class AcmeProperties {
// ... see the preceding example
}
這種配置方式與 SpringApplication 外部的 YAML 配置配合得特別好,如下面示例所示:
# application.yml
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
# additional configuration as required
要使用 @ConfigurationProperties bean,可以用與任何其他 bean 相同的方式注入它們,如下所示:
@Service
public class MyService {
private final AcmeProperties properties;
@Autowired
public MyService(AcmeProperties properties) {
this.properties = properties;
}
//...
@PostConstruct
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
// ...
}
}
提示:使用 @ConfigurationProperties 還可以生成元數(shù)據(jù)文件,IDE 可以使用這些文件為自己的 keys 提供自動完成功能。
第三方配置
除了使用 @ConfigurationProperties 注解類之外,還可以在公共 @Bean 方法上使用它。如果要將屬性綁定到不在你控制范圍內(nèi)的第三方組件,那么這樣做特別有用。
要從 Environment 屬性配置 bean,請將 @ConfigurationProperties 添加到其 bean 注冊中,如下所示:
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
用 another 前綴定義的任何屬性都映射到 AnotherComponent bean,其方式與前面的 AcmeProperties 示例類似。
寬松的綁定
Spring Boot 使用一些寬松的規(guī)則將 Environment 屬性綁定到 @ConfigurationProperties bean,因此 Environment 屬性名和 bean 屬性名之間不需要完全匹配。有用的常見示例包括短劃線分隔的環(huán)境屬性(例如,context-path 綁定到 contextPath)和大寫的環(huán)境屬性(例如,PORT 綁定到 port)。
例如,考慮以下 @ConfigurationProperties 類:
@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
在上面的示例中,以下屬性名都可以使用:
寬松綁定
| 屬性 | 注釋 |
|---|---|
| acme.my-project.person.first-name | 烤串式,推薦在 .properties 和 .yml 文件中使用。 |
| acme.myProject.person.firstName | 標(biāo)準(zhǔn)的駝峰大小寫語法。 |
| acme.my_project.person.first_name | 下劃線表示法,這是在 .properties 和 .yml 文件中使用的另一種格式。 |
| ACME_MYPROJECT_PERSON_FIRSTNAME | 大寫格式,建議在使用系統(tǒng)環(huán)境變量時(shí)使用。 |
注釋:該注解的 prefix 值必須是烤串式(小寫并用“-”分隔,例如:acme.my-project.person)。
每個(gè)屬性源的寬松綁定規(guī)則
| 屬性源 | 簡單的(Simple) | 列表(List) |
|---|---|---|
| 屬性文件 | 駝峰式、烤串式或下劃線式 | 使用“[]”的標(biāo)準(zhǔn)列表語法或逗號分隔值。 |
| YAML 文件 | 駝峰式、烤串式或下劃線式 | 標(biāo)準(zhǔn) YAML 列表語法或逗號分隔值。 |
| 環(huán)境變量 | 以下劃線為分隔符的大寫格式。“_”不應(yīng)在屬性名中使用。 | 下劃線環(huán)繞的數(shù)字值,例如:MY_ACME_1_OTHER = my.acme[1].other |
| 系統(tǒng)屬性 | 駝峰式、烤串式或下劃線式 | 使用“[]”的標(biāo)準(zhǔn)列表語法或逗號分隔值。 |
提示:我們建議盡可能將屬性存儲為小寫的烤串格式,例如:my.property-name=acme。
在綁定到 Map 屬性時(shí),如果 key 包含除小寫字母-數(shù)字字符或“-”之外的任何內(nèi)容,則需要使用方括號,以便保留原始值。如果沒有用 [] 包圍 key,則會刪除不是字母數(shù)字或“-”的任何字符。例如,考慮將以下屬性綁定到 Map:
acme:
map:
"[/key1]": value1
"[/key2]": value2
/key3: value3
上面的屬性將綁定到以 /key1、/key2 和 key3 作為鍵的 Map。
合并復(fù)雜類型
當(dāng)在多個(gè)位置配置列表時(shí),重寫通過替換整個(gè)列表來工作。
例如,假設(shè)一個(gè) MyPojo 對象的 name 和 description 屬性默認(rèn)為空。下面的示例公開來自 AcmeProperties 的 MyPojo 對象列表:
@ConfigurationProperties("acme")
public class AcmeProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
考慮下面的配置:
acme:
list:
- name: my name
description: my description
---
spring:
profiles: dev
acme:
list:
- name: my another name
如果 dev profile 未激活,則 AcmeProperties.list 包含一個(gè) MyPojo 實(shí)體,如前所定義。但是,如果啟用了 dev profile,則 list 仍然只包含一個(gè)實(shí)體(name:my another name,description:null)。此配置不會向列表中添加第二個(gè) MyPojo 實(shí)例,也不會合并實(shí)例。
在多個(gè) profiles 中指定 List 時(shí),將使用優(yōu)先級最高的(且僅使用該 List)。請考慮以下示例:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
acme:
list:
- name: my another name
在上面的示例中,如果 dev profile 已激活,則 AcmeProperties.list 包含一個(gè) MyPojo 實(shí)體(name:my another name ,description:null)。對于 YAML,可以使用逗號分隔的列表和 YAML 列表來完全覆蓋列表的內(nèi)容。
對于 Map 屬性,你可以綁定來自多個(gè)源的屬性值。但是,對于多個(gè)源中的同一屬性,將使用優(yōu)先級最高的屬性。以下示例公開來自 AcmeProperties 的 Map<String,MyPojo>:
@ConfigurationProperties("acme")
public class AcmeProperties {
private final Map<String, MyPojo> map = new HashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
考慮下面的配置:
acme:
map:
key1:
name: my name 1
description: my description 1
spring:
profiles: dev
acme:
map:
key1:
name: dev name 1
key2:
name: dev name 2
description: dev description 2
如果 dev profile 未激活,則 AcmeProperties.map 包含一個(gè)鍵為 key1 的實(shí)體(name:my name 1,description:my description 1)。但是,如果啟用了 dev profile,那么 map 包含兩個(gè)實(shí)體,其中鍵為 key1(name :my name 1,description:my description 1)和 key2(name :my name 2,description:my description 2)。
注釋:前面的合并規(guī)則適用于來自所有屬性源的屬性,而不僅僅是 YAML 文件。
屬性轉(zhuǎn)換
當(dāng) Spring Boot 綁定到 @ConfigurationProperties bean 時(shí),它嘗試將外部應(yīng)用程序?qū)傩詮?qiáng)制轉(zhuǎn)換為正確的類型。如果需要自定義類型轉(zhuǎn)換,可以提供 ConversionService bean(帶有名為 ConversionService 的 bean)或自定義屬性編輯器(通過 CustomEditorConfigurer bean)或自定義 Converters(帶有注解為 @ConfigurationPropertiesBinding 的 bean 定義)。
注釋:由于此 bean 在應(yīng)用程序生命周期的早期被請求,請確保限制 ConversionService 正在使用的依賴項(xiàng)。通常,你需要的任何依賴項(xiàng)在創(chuàng)建時(shí)都可能未完全初始化。如果自定義 ConversionService 對配置鍵強(qiáng)制(coercion)來說不是必須的,并且它僅依賴于使用 @ConfigurationPropertiesBinding 限定的自定義轉(zhuǎn)換器,則可能需要重命名它。
轉(zhuǎn)換持續(xù)時(shí)間
Spring Boot 對表示持續(xù)時(shí)間有專門的支持。如果公開 java.time.Duration 屬性,則應(yīng)用程序?qū)傩灾械囊韵赂袷娇捎茫?/p>
(1)常規(guī)的 long 表示(如果沒有指定 @DurationUnit,則使用毫秒作為默認(rèn)單位)
(2)java.time.Duration 使用的標(biāo)準(zhǔn) ISO-8601 格式
(3)一種更可讀的格式,其中值和單位是結(jié)合在一起的(例如,10s 表示 10 秒)
考慮以下示例:
```
@ConfigurationProperties("app.system")
public class AppSystemProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public void setSessionTimeout(Duration sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
}
```
要指定 30 秒的會話超時(shí),30、PT30S 和 30s 都是等價(jià)的。讀取超時(shí) 500ms 可以用以下任何形式指定:500、PT0.5S 和 500ms。
還可以使用任何受支持的單位。它們是:
(1)ns:納秒
(2)us:微秒
(3)ms:毫秒
(4)s:秒
(5)m:分鐘
(6)h:小時(shí)
(7)d:天
默認(rèn)單位是毫秒,可以使用 @DurationUnit 重寫,如上面的示例所示。
提示:如果你是從簡單使用 Long 來表示持續(xù)時(shí)間的以前版本升級,請確保在切換到 Duration 的同時(shí)定義單位(如果不是毫秒,則使用 @DurationUnit 定義)。這樣做提供了一個(gè)透明的升級路徑,同時(shí)支持更豐富的格式。
轉(zhuǎn)換數(shù)據(jù)大小
Spring Framework 有一個(gè) DataSize 值類型,允許以字節(jié)表示大小。如果公開 DataSize 屬性,則應(yīng)用程序?qū)傩灾械囊韵赂袷娇捎茫?/p>
(1)常規(guī)的 long 表示(如果沒有指定 @DataSizeUnit,則使用字節(jié)作為默認(rèn)單位)
(2)一種更可讀的格式,其中值和單位是結(jié)合在一起的(例如,10MB 表示 10 兆字節(jié))
考慮下面的示例:
@ConfigurationProperties("app.io")
public class AppIoProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
public void setSizeThreshold(DataSize sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
要指定 10 兆字節(jié)的緩沖區(qū)大小,10 和 10MB 是等價(jià)的。256 字節(jié)的大小閾值可以指定為 256 或 256B。
還可以使用任何受支持的單位。它們是:
(1)B:字節(jié)
(2)KB:千字節(jié)
(3)MB:兆字節(jié)
(4)GB:千兆字節(jié)
(5)TB:兆兆字節(jié)
默認(rèn)單位是字節(jié),可以使用 @DataSizeUnit 重寫,如上面的示例所示。
提示:如果你是從簡單使用 Long 來表示大小的以前版本升級,請確保在切換到 DataSize 的同時(shí)定義單位(如果不是字節(jié),則使用 @DataSizeUnit 定義)。這樣做提供了一個(gè)透明的升級路徑,同時(shí)支持更豐富的格式。
@ConfigurationProperties 驗(yàn)證
每當(dāng)使用 Spring 的 @Validated 注解對 @ConfigurationProperties 類進(jìn)行注解時(shí),Spring Boot 就會嘗試驗(yàn)證它們。你可以直接在配置類上使用 JSR-303 javax.validation 約束注解。為此,請確保類路徑上有一個(gè)兼容的 JSR-303 實(shí)現(xiàn),然后將約束注解添加到字段上,如下面示例所示:
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
// ... getters and setters
}
提示:你還可以通過注解 @Bean 方法來觸發(fā)驗(yàn)證,該方法使用 @Validated 創(chuàng)建配置屬性。
雖然在綁定時(shí)也會驗(yàn)證嵌套屬性,但最好還是將關(guān)聯(lián)字段標(biāo)注為 @Valid。這確保即使找不到嵌套屬性,也會觸發(fā)驗(yàn)證。以下示例基于前面的 AcmeProperties 示例:
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// ... getters and setters
public static class Security {
@NotEmpty
public String username;
// ... getters and setters
}
}
你還可以通過創(chuàng)建名為 configurationPropertiesValidator 的 bean 定義來添加自定義 Spring Validator。@Bean 方法應(yīng)當(dāng)聲明為 static。配置屬性驗(yàn)證器是在應(yīng)用程序生命周期的早期創(chuàng)建的,將 @Bean 方法聲明為 static 可以創(chuàng)建 Bean,而無需實(shí)例化 @Configuration 類。這樣做可以避免任何可能由早期實(shí)例化引起的問題。有一個(gè)屬性驗(yàn)證示例,演示了如何設(shè)置。
提示:spring-boot-actuator 模塊包括一個(gè)端點(diǎn),該端點(diǎn)公開所有 @ConfigurationProperties bean。將 web 瀏覽器指向 /actuator/configprops 或使用等價(jià)的 JMX 端點(diǎn)。詳見“生產(chǎn)就緒功能”章節(jié)。
@ConfigurationProperties 和 @Value
@Value 注解是一個(gè)核心容器功能,它不提供與類型安全配置屬性相同的功能。下表總結(jié)了 @ConfigurationProperties 和 @Value 支持的功能:
| 功能 | @ConfigurationProperties | @Value |
|---|---|---|
| 寬松的綁定 | 是 | 否 |
| 元數(shù)據(jù)支持 | 是 | 否 |
| SpEL | 否 | 是 |
如果你為自己的組件定義了一組配置鍵,我們建議你將它們分組到一個(gè)帶有 @ConfigurationProperties 注解的 POJO 中。你還應(yīng)該注意到,由于 @Value 不支持寬松綁定,因此如果你需要使用環(huán)境變量來提供值,那么它就不是一個(gè)好的選擇。
我是代碼匠心,和我一起學(xué)習(xí)更多精彩知識!!!掃描二維碼!關(guān)注我,實(shí)時(shí)獲取推送。


Spring Boot 允許你將配置外部化,以便可以在不同的環(huán)境中使用相同的應(yīng)用程序代碼。可以使用屬性文件、YAML文件、環(huán)境變量和命令行參數(shù)將配置外部化。屬性值可以通過使用 @Value 注解直接注入 bean,可以通過 Spring 的 Environment 抽象訪問,也可以通過 @ConfigurationProperties。
浙公網(wǎng)安備 33010602011771號