Spring Boot 2.0.0參考手冊_中文版_Part IV_24

文章作者:Tyan
博客:noahsnail.com ?|? CSDN ?|? 簡書

24. 外部配置

Spring Boot允許你進行外部化配置,因此可以將同樣的應(yīng)用代碼在不同的環(huán)境中運行。你可以使用屬性文件,YAML文件,環(huán)境變量和命令行參數(shù)來進行外部化配置。屬性值可以使用@Value注解直接注入到你的beans中,通過Spring的Environment抽象或通過@ConfigurationProperties綁定到結(jié)構(gòu)化對象上來訪問。

Spring Boot使用非常特別的PropertySource順序,這個順序的設(shè)計是為了允許值的合理重寫。屬性被認為是按照以下順序:

  1. 根目錄下的開發(fā)工具全局設(shè)置屬性(當開發(fā)工具激活時為~/.spring-boot-devtools.properties)。
  2. 測試中的@TestPropertySource注解。
  3. 測試中的@SpringBootTest#properties注解特性。
  4. 命令行參數(shù)。
  5. SPRING_APPLICATION_JSON中的屬性(環(huán)境變量或系統(tǒng)屬性中的內(nèi)聯(lián)JSON嵌入)。
  6. ServletConfig初始化參數(shù)。
  7. ServletContext初始化參數(shù)。
  8. java:comp/env的JNDI特性。
  9. Java系統(tǒng)屬性 (System.getProperties())。
  10. 操作系統(tǒng)環(huán)境變量。
  11. RandomValuePropertySource只在random.*中有屬性。
  12. jar包之外的指定配置文件的應(yīng)用屬性(application-{profile}.properties和YAML變量)。
  13. jar包之內(nèi)的指定配置文件的應(yīng)用屬性(application-{profile}.properties和YAML變量)。
  14. jar包之外的應(yīng)用屬性 (application.properties和YAML變量)。
  15. jar包之內(nèi)的應(yīng)用屬性 (application.properties和YAML變量)。
  16. @Configuration類中的@PropertySource注解 。
  17. 默認屬性(通過SpringApplication.setDefaultProperties指定).

為了提供一個具體的例子,假設(shè)你開發(fā)了一個使用名字屬性的@Component

import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*

@Component
public class MyBean {

    @Value("${name}")
    private String name;

    // ...

}

在你的應(yīng)用路徑中(例如在你的jar內(nèi)部),你可以使用application.propertiesname提供一個合理的默認屬性值。當在新環(huán)境運行時,application.properties可以在jar外部提供來重寫name;對于一次性測試,你可以通過指定的命令行切換來啟動(例如java -jar app.jar --name="Spring")。

SPRING_APPLICATION_JSON可以在命令行中通過環(huán)境變量提供。例如在UNIX shell中:

$ SPRING_APPLICATION_JSON='{"foo":{"bar":"spam"}}' java -jar myapp.jar

在這個例子中,Spring的Environment中會有foo.bar=spam。你也可以在系統(tǒng)變量中提供JSON作為spring.application.json

$ java -Dspring.application.json='{"foo":"bar"}' -jar myapp.jar

或命令行參數(shù):

$ java -jar myapp.jar --spring.application.json='{"foo":"bar"}'

或JNDI變量java:comp/env/spring.application.json

24.1 配置隨機值

當注入隨機值時,RandomValuePropertySource是很有用的(例如,注入秘密或測試用例)。它可以產(chǎn)生integerslongsuuidsstrings,例如:

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) CLOSEOPEN,CLOSE可以是任何字符,value,max是整數(shù)。如果提供了max,則value是最小值,max是最大值(不包含)。

24.2 訪問命令行屬性

默認情況下,SpringApplication會將任何命令行參數(shù)(以--開頭,例如--server.port=9000)轉(zhuǎn)換成property并將其添加到Spring的Environment中。正如前面提到的那樣,命令行屬性總是優(yōu)先于其它的配置源。

如果你想命令行屬性添加到Environment中,你可以使用SpringApplication.setAddCommandLineProperties(false)禁用它。

24.3 應(yīng)用屬性文件

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

  1. 當前目錄的子目錄/config

  2. 當前目錄

  3. classpath中的/config

  4. classpath的根目錄

這個列表是按優(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.namespring.config.location可以在早期用來決定加載哪一個文件,因此必須被定義為環(huán)境屬性(通常是操作系統(tǒng)環(huán)境,系統(tǒng)屬性或命令行參數(shù))。

如果spring.config.location包含目錄(相對于文件而言),它們應(yīng)該以/結(jié)尾(在加載之前,在后面添加上從spring.config.name中產(chǎn)生的名字,包括指定配置文件的名字)。在spring.config.location中指定的文件按原樣使用,不支持指定配置文件變量,將會被任何指定配置文件的屬性覆蓋。

默認搜索路徑一直用classpath:,classpath:/config,file:,file:config/,不管spring.config.location中的值。搜索路徑按從低到高排序(file:config/最高)。如果你指定了自己的位置,它們優(yōu)先于所有的默認位置并使用同樣的從低到高的順序。這樣你可以在application.properties中為你的應(yīng)用設(shè)置默認值(或你可以選擇spring.config.name的其它生成文件基本名),在運行時用其它的文件覆蓋它,同時保留默認值。

如果你使用環(huán)境變量而不是系統(tǒng)屬性,大多數(shù)操作系統(tǒng)不允許句號分隔的關(guān)鍵字,但你可以用下劃線代替(例如,SPRING_CONFIG_NAME代替spring.config.name)。

如果你在容器中運行,那么JNDI屬性(在java:comp/env中)或servlet上下文初始化參數(shù)也可以用來代替環(huán)境變量或系統(tǒng)屬性。

24.4 特定的profile屬性

除了application.properties文件之外,特定的profile屬性也可以使用命名規(guī)范application-{profile}.properties來定義。Environment有一系列默認配置文件(默認為[default]),如果沒有設(shè)置激活的配置文件,會使用默認的配置文件(例如,如果沒有激活顯式的配置文件,則會加載application-default.properties中的屬性)。

特定的profile屬性會從相同位置加載application.properties,特定的profile文件總是覆蓋非特定的配置文件,無論特定profile文件在你打包的jar內(nèi)部還是外部。

如果指定了幾個配置文件,將會應(yīng)用最后一個。例如,spring.profiles.active屬性指定的配置文件在那些配置的文件之后通過SpringApplication API添加,因此優(yōu)先級更高。

如果你在spring.config.location中指定了任何文件,那些文件的特定profile版本將不會被考慮。如果你也想使用特定的profile屬性,在spring.config.location中使用目錄。

24.5 屬性中的占位符

當使用application.properties中的值時,會通過現(xiàn)有的Environment進行過濾,因此你可以參考前面定義的值(例如從系統(tǒng)屬性中)。

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

你也可以使用這個技術(shù)來創(chuàng)建現(xiàn)有的Spring Boot屬性的short版本。怎樣使用的更多細節(jié)請看70.4小節(jié),“Use ‘short’ command line arguments”。

24.6 使用YAML代替Properties

YAML是JSON的超集,它可以用一種非常方便的形式來指定分層配置數(shù)據(jù)。當你的類路徑有SnakeYAML庫時,SpringApplication類自動支持YAML作為properties的一個替代品。

如果你使用‘Starters’,SnakeYAML將由spring-boot-starter自動提供。

24.6.1 加載YAML

Spring框架提供了兩個類用來方便的加載YAML文檔。YamlPropertiesFactoryBean將加載YAML作為PropertiesYamlMapFactoryBean將加載YAML作為Map

例如,下面的YAML文檔:

environments:
    dev:
        url: http://dev.bar.com
        name: Developer Setup
    prod:
        url: http://foo.bar.com
        name: My Cool App

將被轉(zhuǎn)換成這些屬性:

environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App

YAML列表通過[index]解引用表示為屬性的key,例如這個YAML:

my:
   servers:
       - dev.bar.com
       - foo.bar.com

將被轉(zhuǎn)換成這些屬性:

my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com

為了像使用Spring的DataBinder一樣(@ConfigurationProperties的功能)綁定這些屬性,你需要在類型為java.util.List(或Set)的目標bean中有屬性,你需要提供一個setter或用一個可變的值來對它初始化,例如,綁定上面的屬性值:

@ConfigurationProperties(prefix="my")
public class Config {

    private List<String> servers = new ArrayList<String>();

    public List<String> getServers() {
        return this.servers;
    }
}

24.6.2 在Spring Environment中公開YAML為屬性

YamlPropertySourceLoader類可以在Spring的Environment中將YAML作為PropertySource。這允許你使用熟悉的@Value注解和占位符語法來訪問YAML屬性。

24.6.3 多profile的YAML文檔

你可以在單個文件中指定多個特定profile的YAML文檔,當應(yīng)用文檔時,通過spring.profiles關(guān)鍵字來表明使用哪個文檔。例如:

server:
    address: 192.168.1.100
---
spring:
    profiles: development
server:
    address: 127.0.0.1
---
spring:
    profiles: production
server:
    address: 192.168.1.120

在上面的例子中,如果development profile被激活,server.address的值為127.0.0.1。如果developmentproduction profile不可用,server.address的值為192.168.1.100

當應(yīng)用上下文啟動時,如果沒有顯式的激活profile,則激活默認的profile。因此在這個YAML文件中我們僅在"default" profile中設(shè)置了security.user.password

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

在這個例子中,密碼總是設(shè)置的,因為它沒有添加到如何profile中,必要時我們必須在其它的profile中顯式的對它重新設(shè)置:

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

Spring profiles被設(shè)計為使用"spring.profiles"元素可以選擇性的用!字符進行否定。如果否定的和非否定的profile指向一個單獨的文檔,必須至少匹配一個非否定的profile,可能沒有否定的profile進行匹配。

24.6.4 YAML缺點

YAML文件不能通過@PropertySource注解進行加載。因此在這種情況下如果你需要加載值,你需要使用屬性文件。

24.6.5 合并YAML列表

正如我們上面看到的,任何YAML內(nèi)容最終都要轉(zhuǎn)換成屬性。當通過profile重寫“l(fā)ist“屬性時,這個過程可能有違直覺。

例如,假設(shè)MyPojo對象的namedescription屬性默認情況下為空。從FooProperties使用一個MyPojo列表:

@ConfigurationProperties("foo")
public class FooProperties {

    private final List<MyPojo> list = new ArrayList<>();

    public List<MyPojo> getList() {
        return this.list;
    }

}

考慮下面的配置:

foo:
  list:
    - name: my name
      description: my description
---
spring:
  profiles: dev
foo:
  list:
    - name: my another name

如果dev profile沒有激活,FooProperties.list將包含一個上面定義的MyPojo輸入。然而如果dev profile可用,lists仍只包含一個輸入(name為“my another name”,description為空)。這個配置將不能添加第二個MyPojolist中,并且它將不能合并這些項。

當在多個profiles中指定一個集合時,將會使用最高優(yōu)先級的那個集合(唯一的哪個):

foo:
  list:
    - name: my name
      description: my description
    - name: another name
      description: another description
---
spring:
  profiles: dev
foo:
  list:
     - name: my another name

在上面的例子中,假設(shè)dev profile被激活,FooProperties.list將包含一個MyPojo輸入(name為“my another name”,description為空)。

24.7 類型安全的配置屬性

Boot提供了一種處理屬性的可替代方法,允許強類型的beans管理和驗證你的應(yīng)用的配置。例如:

@ConfigurationProperties(prefix="connection")
public class ConnectionProperties {

    private String username;

    private InetAddress remoteAddress;

    // ... getters and setters

}

建議添加getters和setters,綁定是通過標準的Java Beans屬性描述符,像在Spring MVC中一樣。對于不可變類型或那些從String中可直接強制轉(zhuǎn)換的類型,它們是強制性的。只要它們被初始化,maps,集合或數(shù)組需要getter方法,但不需要setter方法因為通過綁定起它們可以直接變化。如果有setter,可以創(chuàng)建Maps,集合和數(shù)組。Maps和集合可以通過getter擴展,數(shù)組擴展需要setter。如果它們有默認的構(gòu)造函數(shù),或構(gòu)造函數(shù)接收可以從String類型強制轉(zhuǎn)換的值,嵌入的POJO屬性也可以創(chuàng)建(因此setter不是強制性的)。一些人使用Lombok項目來自動添加getter和setter。

?

請看@Value@ConfigurationProperties之間的不同。

你也需要在@EnableConfigurationProperties注解中列出要注冊的屬性類:

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

@ConfigurationProperties以那種方式注冊時,這個bean將有一個常規(guī)的名字:<prefix>-<fqn><prefix>是在@ConfigurationProperties注解中指定的環(huán)境關(guān)鍵字的前綴,<fqn>是bean的完整合格的名字。如果注解沒有提供任何前綴,則只用bean的完整合格的名字。

在上面的例子中bean名字是connection-com.example.ConnectionProperties,假設(shè)ConnectionPropertiescom.example包中。

即使上面的配置會為ConnectionProperties創(chuàng)建一個正規(guī)的bean,我們建議@ConfigurationProperties只處理環(huán)境,特別是不從上下文中注入其它的beans。已經(jīng)說過,為了任何帶有@ConfigurationProperties注解的bean可以根據(jù)Environment屬性進行配置,@EnableConfigurationProperties注解也自動應(yīng)用到你的工程中。確保ConnectionProperties已經(jīng)是一個bean,你可以簡寫上面的MyConfiguration

@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionProperties {

    // ... getters and setters

}

這種風(fēng)格的配置在SpringApplication的外部化YAML配置中工作的尤其好:

# application.yml

connection:
    username: admin
    remoteAddress: 192.168.1.1

# additional configuration as required

為了同@ConfigurationProperties beans一起工作,你可以像任何其它bean一樣以相同的方式注入它們:

@Service
public class MyService {

    private final ConnectionProperties connection;

    @Autowired
    public MyService(ConnectionProperties connection) {
        this.connection = connection;
    }

     //...

    @PostConstruct
    public void openConnection() {
        Server server = new Server();
        this.connection.configure(server);
    }

}

使用@ConfigurationProperties也允許你生成IDEs可以使用的元數(shù)據(jù)文件。更多細節(jié)請看附錄B,配置元數(shù)據(jù)附錄。

24.7.1 第三方配置

也可以使用@ConfigurationProperties來注解一個類,你也可以在公有的@Bean方法上使用它。當你想綁定屬性到你控制之外的第三方組件上時尤其有用。

@ConfigurationProperties(prefix = "foo")
@Bean
public FooComponent fooComponent() {
    ...
}

任何定義的帶有foo前綴的屬性都將以類似于上面的ConnectionProperties例子中的方式映射到FooComponent bean中。

24.7.2 松散綁定

Spring Boot使用一些松散的規(guī)則將Environment屬性綁定到@ConfigurationProperties beans上,因此不需要在Environment屬性名和bean屬性名之間進行確切的匹配。常見的有用例子包括破折號分隔(例如,context-path綁定到contextPath),大小寫(例如PORT綁定到port,)環(huán)境屬性。

例如,給定下面的@ConfigurationProperties類:

@ConfigurationProperties(prefix="person")
public class OwnerProperties {

    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}

下面的屬性名都可以使用:

表24.1. 松散綁定

Property Note
person.firstName 標準的駝峰寫法
person.first-name 破折號注解,建議在.properties.yml文件中使用
person.first_name 下劃線注解,.properties.yml文件中的可替代寫法
PERSON_FIRST_NAME 大寫形式。當使用系統(tǒng)變量時推薦

24.7.3 屬性轉(zhuǎn)換

當Spring綁定屬性到@ConfigurationProperties beans時,它將試圖將外部的應(yīng)用屬性強制轉(zhuǎn)換成正確的類型。如果你需要定制類型轉(zhuǎn)換你可以提供一個ConversionService bean(bean id為conversionService),或定制屬性編輯器(通過CustomEditorConfigurer bean),或定制Converters(帶有@ConfigurationPropertiesBinding注解的bean定義)。

bean要求在應(yīng)用生命周期中的早期,要確保限制ConversionService使用的依賴。通常,任何你要求的依賴可能在創(chuàng)建時不是完整初始化的。如果你定制的ConversionService不要求配置關(guān)鍵字強制轉(zhuǎn)換,你可能想重新命名你定制的ConversionService,并且只依賴滿足@ConfigurationPropertiesBindings的定制轉(zhuǎn)換器。

24.7.4 @ConfigurationProperties驗證

Spring Boot會試圖驗證外部化配置,默認使用JSR-303(如果它在classpath中)。你可以簡單的添加JSR-303 javax.validation約束注解到你的@ConfigurationProperties類中:

@ConfigurationProperties(prefix="connection")
public class ConnectionProperties {

    @NotNull
    private InetAddress remoteAddress;

    // ... getters and setters

}

為了驗證嵌入的屬性值,你必須注解相關(guān)的字段作為@Valid來觸發(fā)它的校驗。例如,在上面的ConnectionProperties例子上構(gòu)建:

@ConfigurationProperties(prefix="connection")
public class ConnectionProperties {

    @NotNull
    @Valid
    private RemoteAddress remoteAddress;

    // ... getters and setters

    public static class RemoteAddress {

        @NotEmpty
        public String hostname;

        // ... getters and setters

    }

}

通過創(chuàng)建一個稱為configurationPropertiesValidator的bean定義,你也可以添加定制的Spring Validator@Bean方法應(yīng)該聲明靜態(tài)的。配置屬性驗證器在應(yīng)用生命周期的早期創(chuàng)建,聲明@Bean方法為靜態(tài)方法,允許不必實例化@Configuration類就創(chuàng)建bean。這避免了任何早期實例化可能引起的問題。這兒有一個屬性驗證的例子因此你可以看一下怎樣設(shè)置它

spring-boot-actuator模塊包含一個端點,這個端點將公開所有的@ConfigurationProperties beans。簡單的將你的web瀏覽器指向/configprops或用等價的JMX端點。更多細節(jié)請看產(chǎn)品級功能

24.7.5 @ConfigurationProperties和@Value

@Value是一種核心的容器功能,它不能作為類型安全配置屬性提供同樣的功能。下面的表中總結(jié)了@ConfigurationProperties@Value支持的功能:

功能 @ConfigurationProperties @Value
松散綁定 Yes No
元數(shù)據(jù)支持 Yes No
SpEL評估 No Yes

如果你為自己的組件定義了一些配置關(guān)鍵字,我們建議你將它們分組到帶有@ConfigurationProperties注解的POJO中。也請注意@Value不支持松散綁定,如果你需要用環(huán)境變量提供值,它不是一個好的選擇。

最后,雖然你可以在@Value中寫表達式,但這種表達式不能從應(yīng)用屬性文件中處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容