從零開始學Spring Boot系列-外部化配置

代码匠心發表於2024-04-10

Spring Boot 允許你將配置外部化,以便可以在不同的環境中使用相同的應用程式程式碼。可以使用屬性檔案、YAML檔案、環境變數和命令列引數將配置外部化。屬性值可以透過使用 @Value 註解直接注入 bean,可以透過 Spring 的 Environment 抽象訪問,也可以透過 @ConfigurationProperties。

Spring Boot 使用一種非常特殊的 PropertySource 順序,其設計目的是允許合理地覆蓋值。屬性按以下順序考慮:

(1)主目錄上的 Devtools 全域性設定屬性(Devtools 處於活動狀態時 ~/.spring-boot-devtools.properties)。
(2)測試中的 @TestPropertySource 註解。
(3)測試中的 properties 屬性。在 @SpringBootTest 和測試註解中提供,用於測試應用程式的特定部分。
(4)命令列引數。
(5)來自 SPRING_APPLICATION_JSON(內嵌在環境變數或系統屬性中的 JSON)的屬性。
(6)ServletConfig 初始化引數。
(7)ServletContext 初始化引數。
(8)來自 java:comp/env 的 JNDI 屬性。
(9)Java 系統屬性(System.getProperties())。
(10)OS(作業系統)環境變數。
(11)僅在 random.* 中具有屬性的 RandomValuePropertySource。
(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 指定)。

為了提供一個具體的示例,假設您開發了一個使用 name 屬性的 @Component,如下面的示例所示:


        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 檔案,該檔案覆蓋 name。對於一次性測試,可以使用特定的命令列開關啟動(例如,java -jar app.jar --name="Spring")。

提示:SPRING_APPLICATION_JSON 屬性可以在帶有環境變數的命令列上提供。例如,可以在 UN*X shell 中使用以下行:

    SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar

在前面的示例中,你在 Spring Environment 中使用 acme.name=test 結尾。你還可以在系統屬性中將 JSON 作為 spring.application.json 提供。

$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar

還可以使用命令列引數提供 JSON,如下面的示例所示:

$ java -jar myapp.jar --spring.application.json='{"name":"test"}'

還可以將 JSON 作為 JNDI 變數提供,如下所示:java:comp/env/spring.application.json 。

配置隨機值

RandomValuePropertySource 用於注入隨機值(例如,在機密或測試用例中)。它可以產生 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 是整數。如果提供 max,那麼 value 是最小值,max 是最大值(不包含 max)。

訪問命令列屬性

預設情況下,SpringApplication 將任何命令列選項引數(即以 -- 開頭的引數,例如:--server.port=9000)轉換為一個 property,並將它們新增到 Spring Environment。如前所述,命令列屬性始終優先於其他屬性源。

如果不希望命令列屬性新增到 Enviroment,則可以使用 SpringApplication.setAddCommandLineProperties(false) 禁用它們。

應用程式屬性檔案

SpringApplication 從以下位置的 application.properties 檔案載入屬性,並將它們新增到 Spring Environment:

(1)當前目錄的 /config 子目錄
(2)當前目錄
(3)類路徑的 /config 包
(4)類路徑的根目錄

列表按優先順序排序(在列表中較高位置定義的屬性覆蓋在較低位置定義的屬性)。

註釋:你還可以使用 YAML('.yml') 檔案作為“.properties”的替換。

如果你不喜歡將 application.properties 作為配置檔名,則你可以透過指定 spring.config.name 環境屬性切換到另一個檔名。還可以使用 spring.config.location 環境屬性(目錄位置或檔案路徑的逗號分隔列表)引用顯示位置。下面的示例顯示如何指定不同的檔名。

java -jar myproject.jar --spring.config.name=myproject

下面的示例顯示如何指定兩個位置:

java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

警告:spring.config.name 和 spring.config.location 很早就用於確定必須載入哪些檔案,因此必須將它們定義為環境屬性(通常是 OS 環境變數、系統屬性或命令列引數)。

如果 spring.config.location 包含目錄(而不是檔案),則它們應該以 / 結尾(並且,在執行時,在載入之前,應該附加從 spring.config.name 生成的名稱,包括特定配置的檔名)。在 spring.config.location 中指定的檔案按原樣使用,不支援特定配置的變體,並且由任何特定配置的屬性重寫。

按照相反的順序搜尋配置位置。預設情況下,配置的位置是 classpath:/,classpath:/config/,file:./,file:./config/。結果搜尋順序如下:

(1)file:./config/
(2)file:./
(3)classpath:/config/
(4)classpath:/

當使用 spring.config.location 配置自定義配置位置時,它們將替換預設位置。例如,如果 spring.config.location 配置了值 classpath:/custom-config/,file:./custom-config/,則搜尋順序如下:

(1)file:./custom-config/
(2)classpath:custom-config/

或者,當使用 spring.config.additional-location 配置自定義配置位置時,除了預設位置之外,還將使用它們。在預設位置之前搜尋其他位置。例如,如果配置了 classpath:/custom-config/,file:./custom-config/,則搜尋順序如下:

(1)file:./custom-config/
(2)classpath:custom-config/
(3)file:./config/
(4)file:./
(5)classpath:/config/
(6)classpath:/

此搜尋順序允許你在一個配置檔案中指定預設值,然後有選擇地在另一個配置檔案中重寫這些值。你可以在預設位置的 application.properties 檔案中為應用程式提供預設值。你可以在其中一個預設位置的 application.properties(或使用 spring.config.name 選擇的任何其他基名稱)中為應用程式提供預設值。然後,可以在執行時使用位於其中一個自定義位置的其他檔案覆蓋這些預設值。

註釋:如果使用環境變數而不是系統屬性,大多數作業系統不允許使用句點分隔的鍵名,但是可以使用下劃線(例如,使用 SPRING_CONFIG_NAME 而不是 spring.config.name)。

註釋:如果你的應用程式在容器中執行,那麼可以使用 JNDI 屬性(在java:comp/env中)或 servlet 上下文初始化引數,而不是環境變數或系統屬性。

特定配置的屬性

除了 application.properties 檔案外,還可以使用以下命名約定定義特定配置的屬性:application-{profile}.properties。Environment 有一組預設配置(預設情況下是 [default]),如果未設定活動配置,則使用這些配置。換句話說,如果沒有顯示啟用配置檔案,則載入 application-default.properties 中的屬性。

特定配置的屬性是從與標準 application.properties 相同的位置載入的。不論特定配置的檔案是在打包的 jar 內部還是外部,特定配置的檔案始終覆蓋非特定的檔案。

如果指定了多個配置檔案,則應用“最後勝出”策略。例如,spring.profiles.active 屬性指定的配置檔案將新增到透過 SpringApplication API 配置的檔案之後,因此具有優先權。

註釋:如果在 spring.config.location 中指定了任何檔案,則不考慮這些檔案的特定配置的變體。如果還想使用特定配置的屬性,請在 spring.config.location 中使用目錄。

屬性中的佔位符

application.properties 中的值在使用時透過現有 Environment 進行篩選,因此可以引用以前定義的值(例如,來自系統屬性的)。

app.name=MyApp
app.description=${app.name} is a Spring Boot application

提示:你還可以使用此技術建立現有 Spring Boot 屬性的“短”變體。

使用 YAML 代替 Properties

YAML 是 JSON 的超集,因此,它是一種用於指定分層配置資料的便捷格式。只要類路徑上有 Snake YAML 庫,SpringApplication 類就會自動支援 YAML 作為 properties 的替代者。

註釋:如果你使用“Starters”,Snake YAML 將由 spring.boot.starter 自動提供。

載入 YAML

Spring Framework 提供了兩個方便的類,可用於載入 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

前面的示例將轉換為以下屬性:

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

前面的示例將轉換為這些屬性:

my.servers[0]=dev.example.com
my.servers[1]=another.example.com

要透過使用 Spring Boot 的 Binder 工具類(這就是 @ConfigurationProperties 所做的)繫結到類似的屬性,需要在目標 bean 中有一個 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 環境中將 YAML 作為屬性公開

YamlPropertySourceLoader 類可用於在 Spring Environment 中將 YAML 公開為 PropertySource。這樣就可以使用帶有佔位符語法的 @Value 註解來訪問 YAML 屬性。

多配置的 YAML 文件

透過使用 spring.profiles 鍵指示文件何時應用,可以在單個檔案中指定多個特定配置的 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 可以包含一個簡單的配置檔名(例如:production)或配置檔案表示式。profile 表示式允許表達更復雜的 profile 邏輯,例如:production & (eu-central|eu-west)。

如果應用程式上下文啟動時沒有顯示啟用配置檔案,則將啟用預設的。因此,在下面的 YAML 中,我們為 spring.security.user.password 設定一個值,它僅在“預設”配置檔案中可用:

server:
  port: 8000
---
spring:
  profiles: default
  security:
    user:
      password: weak

但是,在下面的示例中,始終設定密碼,因為它沒有附加到任何配置檔案,並且必須在所有其他配置檔案中根據需要顯式重置密碼:

server:
  port: 8000
spring:
  security:
    user:
      password: weak

透過使用 spring.profiles 元素指定的 Spring 配置檔案可以透過使用“!”字元取反。如果為單個文件同時指定了否定配置檔案和非否定配置檔案,則必須至少有一個非否定配置檔案匹配,並且不能有否定配置檔案匹配。

YAML 的缺點

無法使用 @PropertySource 註解載入 YAML 檔案。因此,如果需要以這種方式載入值,則需要使用屬性檔案。

在特定配置的 YAML 檔案中使用多個 YAML 文件語法可能會導致意外行為。例如,在名為 application-dev.yml 的檔案中考慮以下配置,其中 dev 配置檔案處於活動狀態:

server:
  port: 8000
---
spring:
  profiles: !test
  security:
    user:
      password: weak

在上面的例子中,profile 否定和 profile 表示式的行為將不符合預期。我們建議你不要將特定配置的 YAML 檔案和多個 YAML 文件組合在一起,而只使用其中的一個。

型別安全的配置屬性

使用 @Value(${property}) 註解注入配置屬性有時會很麻煩,特別是在處理多個屬性或資料本身是分層的情況下。Spring Boot 提供了另一種處理屬性的方法,這種方法允許強型別 bean 控制和驗證應用程式的配置,如下面示例所示:

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,預設值為 false。
(2)acme.remote-address,其型別可以從 String 轉換過來。
(3)acme.security.username,具有巢狀的“security”物件,其名稱由屬性的名稱確定。特別是,返回型別根本沒有使用,它可能是 SecurityProperties。
(4)acme.security.password。
(5)acme.security.roles,包含一個字串集合。

註釋:getter 和 setter 通常是必需的,因為繫結是透過標準的 Java Beans 屬性描述符進行的,就像 Spring MVC 一樣。在下列情況下,可省略 setter:

(1)Maps,只要它們被初始化,就需要一個 getter,但不一定需要 setter,因為繫結器可以對它們進行修改。
(2)可以透過索引(通常使用 YAML)或使用單個逗號分隔值(屬性)訪問集合和陣列。在後一種情況下,setter 是必需的。我們建議始終為這樣的型別新增 setter。如果初始化集合,請確保它不是不可變的(如前一個示例所示)。
(3)如果巢狀的 POJO 屬性被初始化(就像前面例子中 Security 欄位),則 setter 不是必須的。如果希望繫結器使用例項的預設建構函式動態建立它,則需要一個 setter。

有些人使用 Project Lombok 自動新增 getter 和 setter。確保 Lombok 不會為這樣的型別生成任何特定的建構函式,因為容器會自動使用它來例項化物件。

最後,只考慮標準的 Java Bean 屬性,不支援繫結靜態屬性。

你還需要在 @EnableConfigurationProperties 註解中列出要註冊的屬性類,如下面的示例所示:

@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}

註釋:當 @ConfigurationProperties bean 以這種方式註冊時,bean 有一個常規名稱:<prefix>-<fqn>,其中 <prefix> 是在 @ConfigurationProperties 註解中指定的環境鍵字首,<fqn> 是 bean 的完全限定名。如果該註解沒有提供任何字首,則只使用 bean 的全完限定名。

上面例子中的 bean 名稱是 acme-com.example.AcmeProperties。

前面的配置為 AcmeProperties 建立一個常規 bean。我們建議 @ConfigurationProperties 只處理環境,特別是不要從上下文中注入其他 bean。請記住,@EnableConfigurationProperties 註解也會自動應用到你的專案中,以便從 Environment 配置任何帶有 @ConfigurationProperties 註解的現有 bean。不是用 @EnableConfigurationProperties(AcmeProperties.class) 註解 MyConfiguration,你可以使 AcmeProperties 成為一個 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 還可以生成後設資料檔案,IDE 可以使用這些檔案為自己的 keys 提供自動完成功能。

第三方配置

除了使用 @ConfigurationProperties 註解類之外,還可以在公共 @Bean 方法上使用它。如果要將屬性繫結到不在你控制範圍內的第三方元件,那麼這樣做特別有用。

要從 Environment 屬性配置 bean,請將 @ConfigurationProperties 新增到其 bean 註冊中,如下所示:

@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
    ...
}

用 another 字首定義的任何屬性都對映到 AnotherComponent bean,其方式與前面的 AcmeProperties 示例類似。

寬鬆的繫結

Spring Boot 使用一些寬鬆的規則將 Environment 屬性繫結到 @ConfigurationProperties bean,因此 Environment 屬性名和 bean 屬性名之間不需要完全匹配。有用的常見示例包括短劃線分隔的環境屬性(例如,context-path 繫結到 contextPath)和大寫的環境屬性(例如,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 標準的駝峰大小寫語法。
acme.my_project.person.first_name 下劃線表示法,這是在 .properties 和 .yml 檔案中使用的另一種格式。
ACME_MYPROJECT_PERSON_FIRSTNAME 大寫格式,建議在使用系統環境變數時使用。

註釋:該註解的 prefix 值必須是烤串式(小寫並用“-”分隔,例如:acme.my-project.person)。

每個屬性源的寬鬆繫結規則

屬性源 簡單的(Simple) 列表(List)
屬性檔案 駝峰式、烤串式或下劃線式 使用“[]”的標準列表語法或逗號分隔值。
YAML 檔案 駝峰式、烤串式或下劃線式 標準 YAML 列表語法或逗號分隔值。
環境變數 以下劃線為分隔符的大寫格式。“_”不應在屬性名中使用。 下劃線環繞的數字值,例如:MY_ACME_1_OTHER = my.acme[1].other
系統屬性 駝峰式、烤串式或下劃線式 使用“[]”的標準列表語法或逗號分隔值。

提示:我們建議儘可能將屬性儲存為小寫的烤串格式,例如:my.property-name=acme。

在繫結到 Map 屬性時,如果 key 包含除小寫字母-數字字元或“-”之外的任何內容,則需要使用方括號,以便保留原始值。如果沒有用 [] 包圍 key,則會刪除不是字母數字或“-”的任何字元。例如,考慮將以下屬性繫結到 Map:

acme:
  map:
    "[/key1]": value1
    "[/key2]": value2
    /key3: value3

上面的屬性將繫結到以 /key1、/key2 和 key3 作為鍵的 Map。

合併複雜型別

當在多個位置配置列表時,重寫透過替換整個列表來工作。

例如,假設一個 MyPojo 物件的 name 和 description 屬性預設為空。下面的示例公開來自 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 包含一個 MyPojo 實體,如前所定義。但是,如果啟用了 dev profile,則 list 仍然只包含一個實體(name:my another name,description:null)。此配置不會向列表中新增第二個 MyPojo 例項,也不會合並例項。

在多個 profiles 中指定 List 時,將使用優先順序最高的(且僅使用該 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 包含一個 MyPojo 實體(name:my another name ,description:null)。對於 YAML,可以使用逗號分隔的列表和 YAML 列表來完全覆蓋列表的內容。

對於 Map 屬性,你可以繫結來自多個源的屬性值。但是,對於多個源中的同一屬性,將使用優先順序最高的屬性。以下示例公開來自 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 包含一個鍵為 key1 的實體(name:my name 1,description:my description 1)。但是,如果啟用了 dev profile,那麼 map 包含兩個實體,其中鍵為 key1(name :my name 1,description:my description 1)和 key2(name :my name 2,description:my description 2)。

註釋:前面的合併規則適用於來自所有屬性源的屬性,而不僅僅是 YAML 檔案。

屬性轉換

當 Spring Boot 繫結到 @ConfigurationProperties bean 時,它嘗試將外部應用程式屬性強制轉換為正確的型別。如果需要自定義型別轉換,可以提供 ConversionService bean(帶有名為 ConversionService 的 bean)或自定義屬性編輯器(透過 CustomEditorConfigurer bean)或自定義 Converters(帶有註解為 @ConfigurationPropertiesBinding 的 bean 定義)。

註釋:由於此 bean 在應用程式生命週期的早期被請求,請確保限制 ConversionService 正在使用的依賴項。通常,你需要的任何依賴項在建立時都可能未完全初始化。如果自定義 ConversionService 對配置鍵強制(coercion)來說不是必須的,並且它僅依賴於使用 @ConfigurationPropertiesBinding 限定的自定義轉換器,則可能需要重新命名它。

轉換持續時間

Spring Boot 對錶示持續時間有專門的支援。如果公開 java.time.Duration 屬性,則應用程式屬性中的以下格式可用:

(1)常規的 long 表示(如果沒有指定 @DurationUnit,則使用毫秒作為預設單位)
(2)java.time.Duration 使用的標準 ISO-8601 格式
(3)一種更可讀的格式,其中值和單位是結合在一起的(例如,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 秒的會話超時,30、PT30S 和 30s 都是等價的。讀取超時 500ms 可以用以下任何形式指定:500、PT0.5S 和 500ms。

還可以使用任何受支援的單位。它們是:

(1)ns:納秒
(2)us:微秒
(3)ms:毫秒
(4)s:秒
(5)m:分鐘
(6)h:小時
(7)d:天

預設單位是毫秒,可以使用 @DurationUnit 重寫,如上面的示例所示。

提示:如果你是從簡單使用 Long 來表示持續時間的以前版本升級,請確保在切換到 Duration 的同時定義單位(如果不是毫秒,則使用 @DurationUnit 定義)。這樣做提供了一個透明的升級路徑,同時支援更豐富的格式。

轉換資料大小

Spring Framework 有一個 DataSize 值型別,允許以位元組表示大小。如果公開 DataSize 屬性,則應用程式屬性中的以下格式可用:

(1)常規的 long 表示(如果沒有指定 @DataSizeUnit,則使用位元組作為預設單位)
(2)一種更可讀的格式,其中值和單位是結合在一起的(例如,10MB 表示 10 兆位元組)

考慮下面的示例:

@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 兆位元組的緩衝區大小,10 和 10MB 是等價的。256 位元組的大小閾值可以指定為 256 或 256B。

還可以使用任何受支援的單位。它們是:

(1)B:位元組
(2)KB:千位元組
(3)MB:兆位元組
(4)GB:千兆位元組
(5)TB:兆兆位元組

預設單位是位元組,可以使用 @DataSizeUnit 重寫,如上面的示例所示。

提示:如果你是從簡單使用 Long 來表示大小的以前版本升級,請確保在切換到 DataSize 的同時定義單位(如果不是位元組,則使用 @DataSizeUnit 定義)。這樣做提供了一個透明的升級路徑,同時支援更豐富的格式。

@ConfigurationProperties 驗證

每當使用 Spring 的 @Validated 註解對 @ConfigurationProperties 類進行註解時,Spring Boot 就會嘗試驗證它們。你可以直接在配置類上使用 JSR-303 javax.validation 約束註解。為此,請確保類路徑上有一個相容的 JSR-303 實現,然後將約束註解新增到欄位上,如下面示例所示:

@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {

    @NotNull
    private InetAddress remoteAddress;

    // ... getters and setters

}

提示:你還可以透過註解 @Bean 方法來觸發驗證,該方法使用 @Validated 建立配置屬性。

雖然在繫結時也會驗證巢狀屬性,但最好還是將關聯欄位標註為 @Valid。這確保即使找不到巢狀屬性,也會觸發驗證。以下示例基於前面的 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

    }

}

你還可以透過建立名為 configurationPropertiesValidator 的 bean 定義來新增自定義 Spring Validator。@Bean 方法應當宣告為 static。配置屬性驗證器是在應用程式生命週期的早期建立的,將 @Bean 方法宣告為 static 可以建立 Bean,而無需例項化 @Configuration 類。這樣做可以避免任何可能由早期例項化引起的問題。有一個屬性驗證示例,演示瞭如何設定。

提示:spring-boot-actuator 模組包括一個端點,該端點公開所有 @ConfigurationProperties bean。將 web 瀏覽器指向 /actuator/configprops 或使用等價的 JMX 端點。詳見“生產就緒功能”章節。

@ConfigurationProperties 和 @Value

@Value 註解是一個核心容器功能,它不提供與型別安全配置屬性相同的功能。下表總結了 @ConfigurationProperties 和 @Value 支援的功能:

功能 @ConfigurationProperties @Value
寬鬆的繫結
後設資料支援
SpEL

如果你為自己的元件定義了一組配置鍵,我們建議你將它們分組到一個帶有 @ConfigurationProperties 註解的 POJO 中。你還應該注意到,由於 @Value 不支援寬鬆繫結,因此如果你需要使用環境變數來提供值,那麼它就不是一個好的選擇。


我是程式碼匠心,和我一起學習更多精彩知識!!!掃描二維碼!關注我,實時獲取推送。
公眾號

源文來自:https://daimajiangxin.cn

原始碼地址:https://gitee.com/daimajiangxin/springboot-learning

相關文章