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

Spring Boot 允許你將配置外部化睹酌,以便可以在不同的環(huán)境中使用相同的應用程序代碼权谁。可以使用屬性文件憋沿、YAML文件旺芽、環(huán)境變量和命令行參數(shù)將配置外部化。屬性值可以通過使用 @Value 注解直接注入 bean卤妒,可以通過 Spring 的 Environment 抽象訪問甥绿,也可以通過 @ConfigurationProperties。

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

(1)主目錄上的 Devtools 全局設置屬性(Devtools 處于活動狀態(tài)時 ~/.spring-boot-devtools.properties)。
(2)測試中的 @TestPropertySource 注解士复。
(3)測試中的 properties 屬性图谷。在 @SpringBootTest 和測試注解中提供,用于測試應用程序的特定部分阱洪。
(4)命令行參數(shù)便贵。
(5)來自 SPRING_APPLICATION_JSON(內嵌在環(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 之外的特定配置的應用程序屬性(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 指定)。

為了提供一個具體的示例,假設您開發(fā)了一個使用 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 提供一個合理的默認屬性值兜蠕。在新環(huán)境中運行時扰肌,可以在 jar 外部提供 application.properties 文件抛寝,該文件覆蓋 name熊杨。對于一次性測試,可以使用特定的命令行開關啟動(例如盗舰,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 結尾。你還可以在系統(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 失仁。

配置隨機值

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 是整數(shù)在抛。如果提供 max,那么 value 是最小值萧恕,max 是最大值(不包含 max)刚梭。

訪問命令行屬性

默認情況下,SpringApplication 將任何命令行選項參數(shù)(即以 -- 開頭的參數(shù)廊鸥,例如:--server.port=9000)轉換為一個 property望浩,并將它們添加到 Spring Environment。如前所述惰说,命令行屬性始終優(yōu)先于其他屬性源磨德。

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

應用程序屬性文件

SpringApplication 從以下位置的 application.properties 文件加載屬性典挑,并將它們添加到 Spring Environment:

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

列表按優(yōu)先級排序(在列表中較高位置定義的屬性覆蓋在較低位置定義的屬性)酥宴。

注釋:你還可以使用 YAML('.yml') 文件作為“.properties”的替換。

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

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 很早就用于確定必須加載哪些文件肆糕,因此必須將它們定義為環(huán)境屬性(通常是 OS 環(huán)境變量、系統(tǒng)屬性或命令行參數(shù))在孝。

如果 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 選擇的任何其他基名稱)中為應用程序提供默認值澈灼。然后竞川,可以在運行時使用位于其中一個自定義位置的其他文件覆蓋這些默認值店溢。

注釋:如果使用環(huán)境變量而不是系統(tǒng)屬性,大多數(shù)操作系統(tǒng)不允許使用句點分隔的鍵名委乌,但是可以使用下劃線(例如床牧,使用 SPRING_CONFIG_NAME 而不是 spring.config.name)。

注釋:如果你的應用程序在容器中運行遭贸,那么可以使用 JNDI 屬性(在java:comp/env中)或 servlet 上下文初始化參數(shù)戈咳,而不是環(huán)境變量或系統(tǒng)屬性。

特定配置的屬性

除了 application.properties 文件外壕吹,還可以使用以下命名約定定義特定配置的屬性:application-{profile}.properties著蛙。Environment 有一組默認配置(默認情況下是 [default]),如果未設置活動配置算利,則使用這些配置册踩。換句話說泳姐,如果沒有顯示激活配置文件效拭,則加載 application-default.properties 中的屬性。

特定配置的屬性是從與標準 application.properties 相同的位置加載的胖秒。不論特定配置的文件是在打包的 jar 內部還是外部缎患,特定配置的文件始終覆蓋非特定的文件。

如果指定了多個配置文件阎肝,則應用“最后勝出”策略挤渔。例如,spring.profiles.active 屬性指定的配置文件將添加到通過 SpringApplication API 配置的文件之后风题,因此具有優(yōu)先權判导。

注釋:如果在 spring.config.location 中指定了任何文件,則不考慮這些文件的特定配置的變體沛硅。如果還想使用特定配置的屬性眼刃,請在 spring.config.location 中使用目錄。

屬性中的占位符

application.properties 中的值在使用時通過現(xiàn)有 Environment 進行篩選摇肌,因此可以引用以前定義的值(例如擂红,來自系統(tǒng)屬性的)。

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

提示:你還可以使用此技術創(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 提供了兩個方便的類蹦玫,可用于加載 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 環(huán)境中將 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 中贷腕,我們?yōu)?spring.security.user.password 設置一個值,它僅在“默認”配置文件中可用:

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

但是咬展,在下面的示例中泽裳,始終設置密碼,因為它沒有附加到任何配置文件破婆,并且必須在所有其他配置文件中根據(jù)需要顯式重置密碼:

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

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

YAML 的缺點

無法使用 @PropertySource 注解加載 YAML 文件烹笔。因此,如果需要以這種方式加載值抛丽,則需要使用屬性文件谤职。

在特定配置的 YAML 文件中使用多個 YAML 文檔語法可能會導致意外行為。例如亿鲜,在名為 application-dev.yml 的文件中考慮以下配置允蜈,其中 dev 配置文件處于活動狀態(tài):

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

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

類型安全的配置屬性

使用 @Value(${property}) 注解注入配置屬性有時會很麻煩垒探,特別是在處理多個屬性或數(shù)據(jù)本身是分層的情況下妓蛮。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)或使用單個逗號分隔值(屬性)訪問集合和數(shù)組钓辆。在后一種情況下剪验,setter 是必需的肴焊。我們建議始終為這樣的類型添加 setter。如果初始化集合功戚,請確保它不是不可變的(如前一個示例所示)娶眷。
(3)如果嵌套的 POJO 屬性被初始化(就像前面例子中 Security 字段),則 setter 不是必須的啸臀。如果希望綁定器使用實例的默認構造函數(shù)動態(tài)創(chuàng)建它茂浮,則需要一個 setter。

有些人使用 Project Lombok 自動添加 getter 和 setter壳咕。確保 Lombok 不會為這樣的類型生成任何特定的構造函數(shù)席揽,因為容器會自動使用它來實例化對象。

最后谓厘,只考慮標準的 Java Bean 屬性幌羞,不支持綁定靜態(tài)屬性。

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

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

注釋:當 @ConfigurationProperties bean 以這種方式注冊時属桦,bean 有一個常規(guī)名稱:<prefix>-<fqn>,其中 <prefix> 是在 @ConfigurationProperties 注解中指定的環(huán)境鍵前綴他爸,<fqn> 是 bean 的完全限定名聂宾。如果該注解沒有提供任何前綴,則只使用 bean 的全完限定名诊笤。

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

前面的配置為 AcmeProperties 創(chuàng)建一個常規(guī) bean。我們建議 @ConfigurationProperties 只處理環(huán)境讨跟,特別是不要從上下文中注入其他 bean纪他。請記住,@EnableConfigurationProperties 注解也會自動應用到你的項目中晾匠,以便從 Environment 配置任何帶有 @ConfigurationProperties 注解的現(xiàn)有 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 還可以生成元數(shù)據(jù)文件向叉,IDE 可以使用這些文件為自己的 keys 提供自動完成功能。

第三方配置

除了使用 @ConfigurationProperties 注解類之外咳胃,還可以在公共 @Bean 方法上使用它植康。如果要將屬性綁定到不在你控制范圍內的第三方組件,那么這樣做特別有用展懈。

要從 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 標準的駝峰大小寫語法棉钧。
acme.my_project.person.first_name 下劃線表示法,這是在 .properties 和 .yml 文件中使用的另一種格式涕蚤。
ACME_MYPROJECT_PERSON_FIRSTNAME 大寫格式宪卿,建議在使用系統(tǒng)環(huán)境變量時使用。

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

每個屬性源的寬松綁定規(guī)則

屬性源 簡單的(Simple) 列表(List)
屬性文件 駝峰式、烤串式或下劃線式 使用“[]”的標準列表語法或逗號分隔值烦粒。
YAML 文件 駝峰式休溶、烤串式或下劃線式 標準 YAML 列表語法或逗號分隔值。
環(huán)境變量 以下劃線為分隔符的大寫格式撒遣∮寿耍“_”不應在屬性名中使用。 下劃線環(huán)繞的數(shù)字值义黎,例如:MY_ACME_1_OTHER = my.acme[1].other
系統(tǒng)屬性 駝峰式、烤串式或下劃線式 使用“[]”的標準列表語法或逗號分隔值豁跑。

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

在綁定到 Map 屬性時艇拍,如果 key 包含除小寫字母-數(shù)字字符或“-”之外的任何內容狐蜕,則需要使用方括號,以便保留原始值卸夕。如果沒有用 [] 包圍 key层释,則會刪除不是字母數(shù)字或“-”的任何字符。例如快集,考慮將以下屬性綁定到 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 時涤伐,將使用優(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 包含一個 MyPojo 實體(name:my another name 祝迂,description:null)。對于 YAML器净,可以使用逗號分隔的列表和 YAML 列表來完全覆蓋列表的內容型雳。

對于 Map 屬性,你可以綁定來自多個源的屬性值山害。但是纠俭,對于多個源中的同一屬性,將使用優(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 包含一個鍵為 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)古掏。

注釋:前面的合并規(guī)則適用于來自所有屬性源的屬性损话,而不僅僅是 YAML 文件。

屬性轉換

當 Spring Boot 綁定到 @ConfigurationProperties bean 時槽唾,它嘗試將外部應用程序屬性強制轉換為正確的類型丧枪。如果需要自定義類型轉換光涂,可以提供 ConversionService bean(帶有名為 ConversionService 的 bean)或自定義屬性編輯器(通過 CustomEditorConfigurer bean)或自定義 Converters(帶有注解為 @ConfigurationPropertiesBinding 的 bean 定義)。

注釋:由于此 bean 在應用程序生命周期的早期被請求豪诲,請確保限制 ConversionService 正在使用的依賴項顶捷。通常,你需要的任何依賴項在創(chuàng)建時都可能未完全初始化屎篱。如果自定義 ConversionService 對配置鍵強制(coercion)來說不是必須的服赎,并且它僅依賴于使用 @ConfigurationPropertiesBinding 限定的自定義轉換器,則可能需要重命名它交播。

轉換持續(xù)時間

Spring Boot 對表示持續(xù)時間有專門的支持重虑。如果公開 java.time.Duration 屬性,則應用程序屬性中的以下格式可用:

(1)常規(guī)的 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 來表示持續(xù)時間的以前版本升級幕庐,請確保在切換到 Duration 的同時定義單位(如果不是毫秒久锥,則使用 @DurationUnit 定義)。這樣做提供了一個透明的升級路徑异剥,同時支持更豐富的格式瑟由。

轉換數(shù)據(jù)大小

Spring Framework 有一個 DataSize 值類型,允許以字節(jié)表示大小冤寿。如果公開 DataSize 屬性错妖,則應用程序屬性中的以下格式可用:

(1)常規(guī)的 long 表示(如果沒有指定 @DataSizeUnit,則使用字節(jié)作為默認單位)
(2)一種更可讀的格式疚沐,其中值和單位是結合在一起的(例如,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 是等價的亮蛔。256 字節(jié)的大小閾值可以指定為 256 或 256B。

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

(1)B:字節(jié)
(2)KB:千字節(jié)
(3)MB:兆字節(jié)
(4)GB:千兆字節(jié)
(5)TB:兆兆字節(jié)

默認單位是字節(jié)究流,可以使用 @DataSizeUnit 重寫辣吃,如上面的示例所示。

提示:如果你是從簡單使用 Long 來表示大小的以前版本升級芬探,請確保在切換到 DataSize 的同時定義單位(如果不是字節(jié)神得,則使用 @DataSizeUnit 定義)。這樣做提供了一個透明的升級路徑偷仿,同時支持更豐富的格式哩簿。

@ConfigurationProperties 驗證

每當使用 Spring 的 @Validated 注解對 @ConfigurationProperties 類進行注解時,Spring Boot 就會嘗試驗證它們酝静。你可以直接在配置類上使用 JSR-303 javax.validation 約束注解节榜。為此,請確保類路徑上有一個兼容的 JSR-303 實現(xiàn)别智,然后將約束注解添加到字段上宗苍,如下面示例所示:

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

    @NotNull
    private InetAddress remoteAddress;

    // ... getters and setters

}

提示:你還可以通過注解 @Bean 方法來觸發(fā)驗證,該方法使用 @Validated 創(chuàng)建配置屬性薄榛。

雖然在綁定時也會驗證嵌套屬性讳窟,但最好還是將關聯(lián)字段標注為 @Valid。這確保即使找不到嵌套屬性敞恋,也會觸發(fā)驗證丽啡。以下示例基于前面的 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 方法應當聲明為 static耳舅。配置屬性驗證器是在應用程序生命周期的早期創(chuàng)建的碌上,將 @Bean 方法聲明為 static 可以創(chuàng)建 Bean,而無需實例化 @Configuration 類浦徊。這樣做可以避免任何可能由早期實例化引起的問題馏予。有一個屬性驗證示例,演示了如何設置盔性。

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

@ConfigurationProperties 和 @Value

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

功能 @ConfigurationProperties @Value
寬松的綁定
元數(shù)據(jù)支持
SpEL

如果你為自己的組件定義了一組配置鍵突那,我們建議你將它們分組到一個帶有 @ConfigurationProperties 注解的 POJO 中。你還應該注意到构眯,由于 @Value 不支持寬松綁定愕难,因此如果你需要使用環(huán)境變量來提供值,那么它就不是一個好的選擇。


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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末猫缭,一起剝皮案震驚了整個濱河市葱弟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猜丹,老刑警劉巖芝加,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異射窒,居然都是意外死亡藏杖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門轮洋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來制市,“玉大人,你說我怎么就攤上這事弊予∠殚梗” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵汉柒,是天一觀的道長误褪。 經常有香客問我,道長碾褂,這世上最難降的妖魔是什么兽间? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮正塌,結果婚禮上嘀略,老公的妹妹穿的比我還像新娘。我一直安慰自己乓诽,他們只是感情好帜羊,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸠天,像睡著了一般讼育。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稠集,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天奶段,我揣著相機與錄音,去河邊找鬼剥纷。 笑死痹籍,一個胖子當著我的面吹牛,可吹牛的內容都是我干的晦鞋。 我是一名探鬼主播词裤,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼刺洒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吼砂?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤鼎文,失蹤者是張志新(化名)和其女友劉穎渔肩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拇惋,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡周偎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撑帖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓉坎。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胡嘿,靈堂內的尸體忽然破棺而出蛉艾,到底是詐尸還是另有隱情,我是刑警寧澤衷敌,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布勿侯,位于F島的核電站,受9級特大地震影響缴罗,放射性物質發(fā)生泄漏助琐。R本人自食惡果不足惜丈钙,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一邑飒、第九天 我趴在偏房一處隱蔽的房頂上張望彤恶。 院中可真熱鬧寇僧,春花似錦允华、人聲如沸压汪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽禀横。三九已至屁药,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柏锄,已是汗流浹背酿箭。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趾娃,地道東北人缭嫡。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像抬闷,于是被迫代替她去往敵國和親妇蛀。 傳聞我的和親對象是個殘疾皇子耕突,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容