Spring Boot容許你外部化你的配置,這樣你就能夠在不一樣的環境中使用相同的應用程序代碼,你可使用properties文件、YAML文件、環境變量和命令行參數來外部化配置,屬性值能夠經過使用@Value
註解直接注入到你的bean中,經過Spring的Environment
抽象訪問,或者經過@ConfigurationProperties
綁定到結構化對象。html
Spring Boot使用一種很是特殊的PropertySource
命令,該命令旨在容許對值進行合理的覆蓋,屬性按如下順序考慮:java
~/.spring-boot-devtools.properties
當devtools處於激活狀態時。SPRING_APPLICATION_JSON
(嵌入在環境變量或系統屬性中的內聯JSON)的屬性ServletConfig
初始化參數ServletContext
初始化參數java:comp/env
中的JNDI屬性System.getProperties()
)random.*
屬性的RandomValuePropertySource
application-{profile}.properties
和YAML
變體)application-{profile}.properties
和YAML
變體)application.properties
和YAML
變體)application.properties
和YAML
變體)@Configuration
類上SpringApplication.setDefaultProperties
指定)爲了提供一個具體的示例,假設你開發了一個使用name
屬性的@Component
,以下例所示:git
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」
)。github
SPRING_APPLICATION_JSON
屬性能夠在帶有環境變量的命令行上提供,例如,你能夠在UN*X shell中使用如下一行:
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
在前面的示例中,在SpringEnvironment
中你最終獲得了acme.name=test
,你也能夠提供JSON做爲spring.applicatio.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
用於注入隨機值(例如,在secrets或測試用例中),它能夠生成integer
、long
、uuid
或string
,以下面的示例所示:web
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
是最大值(惟一的)。spring
在默認狀況下,SpringApplication
會轉換任何命令行選項參數(也就是說,參數從--
開始,像--server.port=9000
)到一個property
,並將它們添加到Spring Environment
中,如前所述,命令行屬性老是優先於其餘屬性源。shell
若是不但願將命令行屬性添加到Environment
中,你可使用SpringApplication.setAddCommandLineProperties(false)
禁用它們。json
SpringApplication
在如下位置從application.properties
文件加載屬性並將它們添加到Spring Environment
:segmentfault
/config
/config
包列表按優先順序排序(在列表中較高的位置定義的屬性覆蓋在較低位置定義的屬性)。api
你還可使用
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/
。由此產生的搜索順序以下:
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
配置時,除了默認位置外,還使用它們,在默認位置以前搜索額外的位置。例如,若是額外位置classpath:/custom-config/
、file:./custom-config/
被配置,搜索順序以下:
file:./custom-config/
classpath:custom-config/
file:./config/
file:./
classpath:/config/
classpath:/
這個搜索排序容許你在一個配置文件中指定默認值,而後在另外一個配置文件中選擇性地覆蓋這些值。你能夠在application.properties
爲你的應用程序提供默認值(或你在spring.config.name
中選擇的其餘basename)位於默認位置之一。這些默認值能夠在運行時被重寫,並在一個定製的位置中放置一個不一樣的文件。
若是你使用環境變量而不是系統屬性,大多數操做系統都不容許使用週期分隔的鍵名,可是你可使用下劃線(例如,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.locaction
中指定了任何文件,不考慮這些文件的特定配置文件的變體。若是你還想同時使用特殊配置文件,在spring.config.location
中使用目錄
application.properties
中的值在使用時經過現有Environment
進行過濾,所以你能夠返回到之前定義的值(例如,來自系統屬性)。
app.name=MyApp app.description=${app.name} is a Spring Boot application
你還可使用此技術來建立現有Spring boot屬性的「Short」變體,詳細信息請參閱第74.4節「使用‘Short’命令行參數」。
YAML是JSON的超集,所以是指定分層配置數據的一種方便的格式,只要類路徑上有SnakeYAML庫,SpringApplication
類就會自動支持YAML做爲屬性的替代。
若是你使用「Starters」,
SnakeYAML將由
spring-boot-starter
自動提供。
Spring框架提供了兩個方便的類,能夠用來加載YAML文檔。YamlPropertiesFactoryBean
以屬性裝載YAML,而YamlMapFactoryBean
以Map
的形式裝載YAML。
例如,參考如下YAML文檔:
environments: dev: url: http://dev.example.com name: Developer Setup prod: url: http://another.example.com name: My Cool App
前面的示例將轉換爲如下屬性:
environments.dev.url=http://dev.example.com environments.dev.name=Developer Setup environments.prod.url=http://another.example.com environments.prod.name=My Cool App
YAML列表表示爲[index]
解釋器的屬性鍵,例如,參考如下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; } }
YamlPropertySourceLoader
類可用於在Spring Environment
中將YAML公開爲PropertySource
,這樣作可使用帶有佔位符語法的@Value
註解來訪問YAML屬性。
你可使用spring.profiles
鍵在單個文件中指定多個特殊配置YAML文檔來指示你文檔什麼時候應用,以下所示:
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
配置文件是激活的,server.address
屬性是127.0.0.1
。相似地,若是production
配置文件是激活的,server.address
屬性是192.168.1.120
。若是不啓用development
和production
配置文件,則屬性的值爲192.168.1.100
。
若是在應用程序上下文啓動時沒有顯式激活,默認配置文件被激活。所以,在接下來的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
註解加載,所以,在須要以這種方式加載值的狀況下,須要使用屬性文件。
使用@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定義瞭如下屬性:
acme.enabled
,默認值爲false
acme.remote-address
,能夠從String
強轉的類型acme.security.username
,使用嵌套的「 security」對象,該對象的名稱由屬性的名稱決定,特別是,返回類型根本不使用,而且多是SecurityProperties
。acme.security.password
acme.security.roles
,使用一個String
集合getter和setter一般是強制性的,由於綁定是經過標準的Java bean屬性描述符,就像在Spring MVC中同樣,在如下狀況下能夠省略setter:
- Map,只要初始化了它們,就須要一個getter,但不必定須要setter,由於綁定器能夠對它們進行轉變。
- 能夠經過索引(一般是YAML)或使用單個逗號分隔值(屬性)來訪問集合和數組,在後一種狀況下,setter是必需的。咱們建議始終爲此類類型添加一個setter,若是初始化一個集合,請確保它不是不可變的(如前面的示例所示)。
- 若是初始化嵌套POJO屬性(如前面示例中的
Security
字段),則不須要setter,若是你但願綁定器使用它的默認構造函數來動態建立實例,那麼你須要一個setter。有些人使用Lombok項目自動添加getter和setter,確保Lombok沒有爲此類類型生成任何特定的構造函數,由於容器會自動使用它來實例化對象。
最後,只考慮標準的Java Bean屬性,不支持對靜態屬性的綁定。
還能夠查看@Value
和@ConfigurationProperties
之間的差別。
你還須要列出要在@EnableConfigurationProperties
註解中註冊的屬性類,以下例所示:
@Configuration @EnableConfigurationProperties(AcmeProperties.class) public class MyConfiguration { }
當以@ConfigurationProperties
這種方式註冊bean時,bean有一個常規名稱:<prefix>-<fqn>
,其中<prefix>
是在@ConfigurationProperties
註解中指定的環境key前綴,<fqn>
是bean的徹底限定名,若是註解不提供任何前綴,則只使用bean的徹底限定名。上面示例中的bean名稱是
acme-com.example.AcmeProperties
。
即便前面的配置爲AcmeProperties
建立了一個常規bean,咱們建議@ConfigurationProperties
只處理環境,特別是從上下文中不注入其餘bean。話雖如此,@EnableConfigurationProperties
註解也自動應用於你的項目,使任何已添加@ConfigurationProperties
註解的bean均可以從Environment
中配置。你能夠經過確保AcmeProperties
已是一個bean來簡化MyConfiguration
,以下面的示例所示:
@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使用的元數據文件,爲你本身的鍵提供自動完成。詳細信息請參閱
附錄B,配置元數據附錄。
除了使用@ConfigurationProperties
來註解類以外,還能夠在公開的@Bean
方法中使用它,當你但願將屬性綁定到控件以外的第三方組件時,這樣作特別有用。
要從Environment
屬性配置bean,請向其bean註冊中添加@ConfigurationProperties
,以下例所示:
@ConfigurationProperties(prefix = "another") @Bean public AnotherComponent anotherComponent() { ... }
用another
前綴定義的任何屬性都被映射到與前面的AcmeProperties
示例相似的另外一個組件bean。
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
)。
表24.2,放寬了每一個屬性源的綁定規則
屬性源 | 樣例 | 列表 |
---|---|---|
屬性文件 | 駝峯式大小寫,鏈接符大小寫,或下劃線符號 | 使用[] 或逗號分隔值的標準列表語法 |
YAML文件 | 駝峯式大小寫,鏈接符大小寫,或下劃線符號 | 標準的YAML列表語法或逗號分隔的值 |
環境變量 | 如下劃線做爲分隔符的大寫格式,不該該在屬性名中使用_ |
由下劃線包圍的數值,例如MY_ACME_1_OTHER = my.acme[1].other |
系統屬性 | 駝峯式大小寫,鏈接符大小寫,或下劃線符號 | 使用[] 或逗號分隔值的標準列表語法 |
咱們建議,在可能的狀況下,屬性以小寫的鏈接符格式存儲,例如
my.property-name=acme
在綁定到Map
屬性時,若是key
不包含小寫字母數字字符或-
,則須要使用括號符號,以便保留原始值。若是鍵沒有被[]
包圍,任何非字母數字或-
的字符都會被刪除,例如,考慮將如下屬性綁定到Map
:
acme: map: "[/key1]": value1 "[/key2]": value2 /key3: value3
上面的屬性將綁定到以/key1
、/key2
和key3
做爲Map中的鍵的Map
。
當在多個地方配置列表時,經過替換整個列表來重寫。
例如,假設一個MyPojo
對象的name
和description
屬性默認爲null
,下面的示例公開了來自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
配置文件不是激活的,AcmeProperties.list
包含一個MyPojo
條目,正如前面定義的。若是啓用了dev
配置文件,然而,該列表仍然只包含一個條目,(以my another name
的名稱和null
的描述),此配置不向列表添加第二個MyPojo
實例,也不合並條目。
當一個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
配置文件是激活的,AcmeProperties.list
包含一個MyPojo
條目(以my another name
做爲name和null
的description),對於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
配置文件不是激活的,AcmeProperties.map
包含一個鍵key1
的條目(一個my name 1
的name和一個my description 1
的description)。若是啓用了dev
配置文件,然而,map
包含鍵key1
(name爲dev name 1
,description爲my description 1
)和key2
(name爲dev name 2
,description爲dev description 2
)的兩個條目。
前面的合併規則適用於來自全部屬性源的屬性,而不只僅是YAML文件。
Spring Boot試圖在綁定到@ConfigurationProperties
bean時將外部應用程序屬性強制轉換到正確的類型,若是須要自定義類型轉換,你能夠提供一個ConversionService
bean(一個名爲ConversionService
的bean)或自定義屬性編輯器(經過一個CustomEditorConfigurer
bean)或自定義Converters
(使用@ConfigurationPropertiesBinding
註解定義的bean)。
由於這個bean在應用程序生命週期的早期就被請求,確保限制你的ConversionService
正在使用的依賴項。
一般,你須要的任何依賴項在建立時可能不會被徹底初始化,若是配置鍵強制轉換不須要自定義ConversionService
,而且只依賴於使用@ConfigurationPropertiesBinding
限定的自定義轉換器,那麼你可能但願重命名自定義轉換服務。
Spring Boot支持表示持續時間,若是你公開一個java.time.Duration
屬性,在應用程序屬性中有如下格式:
long
表示(使用毫秒做爲默認單位,除非指定了@DurationUnit
)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
。
你還可使用任何受支持的單元,這些都是:
ns
ms
s
m
h
d
默認的單位是毫秒,可使用@DurationUnit
覆蓋,如上面的示例所示。
若是你正在升級之前的版本,只是使用Long
來表示持續時間,若是不是在切換到Duration
時的毫秒數,確保定義單元(使用@DurationUnit
)。這樣作能夠提供透明的升級路徑,同時支持更豐富的格式。
每當使用Spring的@validate
註解@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
方法聲明爲靜態,這樣就能夠建立bean,而沒必要實例化@Configuration
類。這樣作能夠避免早期實例化可能致使的任何問題。這裏有一個屬性驗證示例,展現瞭如何設置。
spring-boot-actuator
模塊包含公開全部@ConfigurationProperties
bean的端點,將web瀏覽器指向/actuator/configprops
或使用等效的JMX端點。有關詳細信息,請參閱「 生產就緒特性」一節。
@Value
註解是一個核心容器特性,它不提供與類型安全配置屬性相同的特性,下表總結了@ConfigurationProperties
和@Value
支持的特性:
特性 | @ConfigurationProperties |
@Value |
---|---|---|
寬鬆綁定 | YES | NO |
元數據支持 | YES | NO |
SpEL 評估 |
NO | YES |
若是你爲本身的組件定義了一組配置鍵,咱們建議你將它們分組到帶有@ConfigurationProperties
註解的POJO中。你還應該注意,因爲@Value
不支持寬鬆綁定,所以若是你須要經過使用環境變量來提供值,那麼它不是一個很好的選擇。
最後,當你能夠在@Value
中編寫SpEL
表達式,這些表達式不會從應用程序屬性文件中處理。