文章目录
SpringBoot属性配置(V2.0.2)
Spring Boot使您可以外部化配置,以便可以在不同环境中使用相同的应用程序代码。您可以使用properties
文件,YAML
文件,环境变量和命令行参数来外部化配置。可以使用@Value批
注将属性值直接注入到您的bean
中,可以通过Spring
的Environment
访问,也可以通过@ConfigurationProperties
绑定属性到对象。
Spring Boot
使用一个非常特殊的PropertySource
顺序,按以下顺序序号值小的可以覆盖序号值大的属性:
- 主目录上的
Devtools
全局设置属性(在devtools
处于活动状态时,为〜/ .spring-boot-devtools.properties
) - 测试用到的
@TestPropertySource
注解 - 测试用到的
@SpringBootTest#properties
注解 - 命令行参数,
eg: --debug name=zhangsan
- 来自
SPRING_APPLICATION_JSON
属性 ServletConfig
初始化参数ServletContext
处室话参数JNDI
属性java:comp/env
java
系统属性(System.getProperties
)OS
操作系统属性RandomValuePropertySource
属性,只有random.*
的属性jar
包之外的(application-{profile}.properties
或者yaml
,yml
)属性jar
包内的(application-{profile}.properties
或者yaml
,yml
)属性jar
包之外的(application.properties
或者yaml
,yml
)属性jar
包内的(application.properties
或者yaml
,yml
)属性@Configuration
注解类上的@PropertySource
注解的属性- 默认属性设置(
SpringApplication.setDefaultProperties
设置)
SPRING_APPLICATION_JSON Properties
命令行中使用环境变量来提供SPRING_APPLICATION_JSON
属性:
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
还可以在System
属性中将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
Random Properties
RandomValuePropertySource
可用于注入随机值(例如,测试用例中)。它可以产生integers,longs,uuid或strings
,如以下示例所示:
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值(,max)CLOSE
,其中OPEN,CLOSE
是任何字符,而value,max
是整数。如果提供了max
,则value
是最小值,而max
是最大值(不包括)。
Command Line Properties
默认情况下,SpringApplication
将所有命令行选项参数(即以--
开头的参数,例如--server.port = 9000
)转换为property
,并将其添加到Spring Environment
中。如前所述,命令行属性始终优先于其他属性源。
如果您不希望将命令行属性添加到Environment
,则可以使用SpringApplication.setAddCommandLineProperties(false)
禁用它们。
Properties Files
SpringApplication
从位于以下位置的application.properties
文件中加载属性(也可以使用YAML(.yml)文件来替代.properties。),并将它们添加到Spring Environment
中:
- 当前目录的
/ config
子目录(应该是待执行的jar
包所处位置为当前目录) - 当前目录(应该是待执行的
jar
包所处位置为当前目录) classpath
的/config
子目录classpath
根目录
以上按序号排序,序号小的加载的属性将会覆盖序号大的属性值
如果您不喜欢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
生成的名称,包括profile-specific
文件的文件名)。 spring.config.location
中指定的文件按原样使用,不支持profile-specific
的变体,并且被任何profile-specific
的属性覆盖。
配置位置以相反的顺序搜索。默认情况下,配置的位置是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
选择的其他任何基本名称)中为应用程序提供默认值。然后,可以在运行时使用自定义位置之一中的其他文件覆盖这些默认值。
Profile-specific Properties
除了application.properties
文件,还可以使用以下命名约定来定义特定于配置文件的属性:application- {profile} .properties
。如果没有设置活动配置文件,则环境具有一组默认配置文件(默认为[default
])。换句话说,如果未显式激活任何概要文件,那么将加载来自application-default.properties
的属性。
Profile-specific
的属性是从与标准application.properties
相同的位置加载的,Profile-specific
的文件总是会覆盖非特定文件,无论特定于配置文件的文件是在打包jar的内部还是外部。
如果指定了多个配置文件,则采用后赢策略。例如,在通过SpringApplication API
配置的配置文件之后,添加了spring.profiles.active
属性指定的配置文件,因此具有优先权。
Placeholders in Properties
使用application.properties
中的值时,它们会通过现有的Environment
进行过滤,因此您可以参考以前定义的值(例如,从“系统”属性中)。
app.name=MyApp
app.description=${app.name} is a Spring Boot application
Using YAML Instead of Properties
YAML
是JSON
的超集,因此是一种用于指定层次结构配置数据的便捷格式。只要在类路径上具有SnakeYAML
库(spring-boot-starter
会自动提供SnakeYAML
),SpringApplication
类就会自动支持YAML
作为属性的替代方法。
Loading YAML
Spring Framework
提供了两个方便的类,可用于加载YAML
文档。 YamlPropertiesFactoryBean
将YAML
作为Properties
加载,而YamlMapFactoryBean
将YAML
作为Map
加载。
例如,考虑以下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
所做的)绑定到类似的属性,您需要在类型为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;
}
}
Exposing YAML as Properties in the Spring Environment
YamlPropertySourceLoader
类可用于在Spring
环境中将YAML
公开为PropertySource
。这样做可以让您使用@Value
批注和占位符语法来访问YAML
属性。
Multi-profile YAML Documents
您可以使用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
设置了一个值,该值仅在“default”
配置文件中可用:
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak
而在以下示例中,始终设置密码是因为该密码未附加到任何配置文件,并且必须根据需要在所有其他配置文件中将其显式重置:
server:
port: 8000
spring:
security:
user:
password: weak
使用spring.profiles元素指定的配置可以选择使用!来否定。特点。如果为单个文档同时指定了否定的配置文件和否定的配置文件,则至少一个非否定的配置文件必须匹配,并且否定的配置文件不能匹配。
YAML Shortcomings
无法使用@PropertySource
批注来加载YAML
文件。因此,在需要以这种方式加载值的情况下,需要使用properties
文件。
Type-safe Configuration Properties
使用@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
acme.remote-address
acme.security.username
acme.security.password
.acme.security.roles
您还需要列出要在@EnableConfigurationProperties
中注册的属性类,如以下示例所示:
@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
以这种方式注册@ConfigurationProperties Bean时,该Bean具有常规名称:-,其中是@ConfigurationProperties批注中指定的环境密钥前缀,而是的完全限定名称。bean。如果注释不提供任何前缀,则仅使用Bean的完全限定名称。 上例中的Bean名称为acme-com.example.AcmeProperties。
即使前面的配置为AcmeProperties
创建了常规bean
,我们也建议@ConfigurationProperties
仅处理环境,尤其不要从上下文中注入其他bean
。话虽如此,@EnableConfigurationProperties
批注也会自动应用到您的项目中,以便从环境配置任何使用@ConfigurationProperties
批注的现有bean
。您可以通过确保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());
// ...
}
}
Third-party Configuration
除了使用@ConfigurationProperties
注释类之外,您还可以在公共@Bean
方法上使用它。当您要将属性绑定到控件之外的第三方组件时,这样做特别有用。
要从Environment
属性配置Bean
请将@ConfigurationProperties
添加到其Bean
注册中,如以下示例所示:
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
用另一个前缀定义的任何属性都以类似于前面的AcmeProperties
示例的方式映射到该AnotherComponent bean
。
Relaxed Binding
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;
}
}
在前面的示例中,可以全部使用以下属性名称:
Property | Note |
---|---|
acme.my-project.person.first-name | Kebab大小写,建议在.properties和.yml文件中使用。 |
acme.myProject.person.firstName | 标准驼峰式语法。 |
acme.my_project.person.first_name | 下划线表示法,是.properties和.yml文件中使用的另一种格式。 |
ACME_MYPROJECT_PERSON_FIRSTNAME | 大写格式,在使用系统环境变量时建议使用。 |
注释的前缀值必须为kebab(小写,并用-分隔,例如acme.my-project.person)。
Property Source | Simple | List |
---|---|---|
Properties Files | 驼峰方案,Kebab大小写或下划线表示法 | 使用[]或逗号分隔值的标准列表语法 |
YAML Files | 驼峰方案,Kebab大小写或下划线表示法 | 标准YAML列表语法或逗号分隔的值 |
Environment Variables | 以下划线作为定界符的大写格式。 _不应在属性名称中使用 | 下划线括起来的数值,例如MY_ACME_1_OTHER = my.acme [1] .other |
System properties | 驼峰方案,Kebab大小写或下划线表示法 | 使用[]或逗号分隔值的标准列表语法 |
我们建议,如果可能,属性以小写的kebab格式存储,例如my.property-name = acme。
Merging Complex Types
如果在多个位置配置了列表,则通过替换整个列表来进行覆盖。
例如,假设MyPojo
对象的名称和描述属性默认为空。下面的示例从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
如果开发人员配置文件未处于活动状态,则AcmeProperties.list包含一个MyPojo
条目,如先前所定义。但是,如果启用了开发配置文件,则该列表仍仅包含一个条目(名称为my another name
,并且description
为null
)。此配置不会将第二个MyPojo
实例添加到列表中,并且不会合并项目。
在多个配置文件中指定列表时,将使用优先级最高的列表(并且仅使用那个列表)。考虑以下示例:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
acme:
list:
- name: my another name
在前面的示例中,如果开发人员配置文件处于活动状态,则AcmeProperties.list包含一个MyPojo
条目(其名称为my,另一个名称,并描述为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
如果开发人员配置文件未处于活动状态,则AcmeProperties.map包含一个键为key1的条目(名称为我的名字1,描述为我的描述1)。但是,如果启用了开发配置文件,则map包含两个条目,其中键为key1(名称为dev name 1,描述我的描述1)和key2(名称为dev name 2,描述dev的描述2) 。
Properties Conversion
当Spring Boot
绑定到@ConfigurationProperties bean
时,它尝试将外部应用程序属性强制为正确的类型。如果需要自定义类型转换,则可以提供一个ConversionService bean
(具有名为conversionService的bean
)或自定义属性编辑器(通过CustomEditorConfigurer bean
)或自定义Converters
(具有定义为@ConfigurationPropertiesBinding的bean
定义)。
由于在应用程序生命周期中非常早就请求了此bean,因此请确保限制您的ConversionService使用的依赖项。通常,您需要的任何依赖项在创建时可能都没有完全初始化。如果配置键强制不需要自定义的转换服务,而仅依赖于具有@ConfigurationPropertiesBinding限定条件的自定义转换器,则可能要重命名自定义的转换服务。
Spring Boot
为表达持续时间提供了专门的支持。如果公开java.time.Duration
属性,则应用程序属性中的以下格式可用:
- 常规的
long
表示形式(使用毫秒作为默认单位,除非已指定@DurationUnit
) java.util.Duration
使用的标准ISO-8601
格式- 值和单位相结合的更易读的格式(例如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
for nanosecondsms
for millisecondss
for secondsm
for minutesh
for hoursd
for days
默认单位是毫秒,可以使用@DurationUnit
覆盖,如上面的示例所示。
@ConfigurationProperties Validation
每当使用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
}
您还可以通过使用@Validated注释创建配置属性的@Bean方法来触发验证。
尽管嵌套属性也将在绑定时进行验证,但最好还是将关联的字段注释为@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
方法应声明为静态。配置属性验证器是在应用程序生命周期的早期创建的,并且将@Bean
方法声明为static
可以使创建该Bean而不必实例化@Configuration
类。这样做避免了由早期实例化引起的任何问题。有一个属性验证示例,显示了如何进行设置。
@ConfigurationProperties vs. @Value
@Value
批注是核心容器功能,它没有提供与类型安全的配置属性相同的功能。下表总结了@ConfigurationProperties和@Value
支持的功能:
Feature | @ConfigurationProperties | @Value |
---|---|---|
Relaxed binding | Yes | No |
Meta-data support | Yes | No |
SpEL evaluation | No | Yes |
如果您为自己的组件定义了一组配置键,我们建议您将它们组合在以@ConfigurationProperties注释的POJO中。您还应该意识到,由于@Value不支持宽松的绑定,因此如果您需要使用环境变量来提供值,则它不是一个很好的选择。
最后,尽管您可以在@Value中编写SpEL表达式,但不会从应用程序属性文件中处理此类表达式。
参考文献
https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/html/boot-features-external-config.html