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
表达式,这些表达式不会从应用程序属性文件中处理。