24. 外部配置

Spring Boot 使你可以将配置外部化,以便在不同的环境中使用相同的应用程序代码。你可以使用 properties 文、YAML 文、环境变量和命令行参数来对配置进行外部化。属性值可以通过 @Value 注解直接注入到 bean 中,通过 Spring 的 Environment 抽象访问,或通过 @ConfigurationProperties 绑定到结构化对象。

Spring Boot 使用一个非常特殊的 PropertySource 顺序,该顺序设计成允许值的合理覆盖。属性按以下顺序考虑:

  1. 在你的 home 目录的 Devtools 全局设置属性(~/.spring-boot-devtools.properties 当开发者工具激活时)。
  2. 在你测试上的 @TestPropertySource 注解。
  3. 在你测试上的 properties 属性。可在 @SpringBootTest 和测试注解用于测试特定的应用程序切片。
  4. 命令行参数。
  5. 来自 SPRING_APPLICATION_JSON(内嵌 JSON 的环境变量或系统属性)的属性。
  6. ServletConfig 初始化参数。
  7. ServletContext 初始化参数。
  8. 来自 java:comp/env 的 JNDI 属性。
  9. Java 系统属性 (System.getProperties())。
  10. OS 环境变量。
  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 指定)。

为了提供一个具体的例子,假设你开发了一个使用 name 属性的 @Component,如下面的示例所示:

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

@Component
public class MyBean {

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

    // ...

}

在你的应用程序类路径(例如,在 jar 中)上可以有一个为 name 提供合理的默认值的 application.properties 文件。当在新环境中运行时,可以在 jar 之外提供一个覆写 name 的 application.properties 文件。对于一次性测试,可以使用特定的命令行来改变(例如,java -jar app.jar --name="Spring")。

[Tip] Tip

可以在命令行上使用环境变量提供 SPRING_APPLICATION_JSON 属性。例如,可以在 UN*X shell 中使用以下行:

$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar

在前面的示例中,你在 Spring Environment 中以 acme.name=test 结束。你还可以将类似 spring.application.json 的 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。

24.1 配置随机值

RandomValuePropertySource 对于注入随机值是很有用的(例如,密钥或测试用例)。它可以产生 integers、longs、uuids 或 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 value (,max) CLOSE,其中 OPEN,CLOSE 可以是任何字符,value,max 是 integers。如果提供 max,那么 value 是最小值,max 是最大值(不包含在内)。

24.2 访问命令行属性

默认情况下,SpringApplication 会将所有命令行选项参数 (即,参数以 -- 开始,例如 --server.port=9000) 转换为属性,并将它们添加到 Spring Environment 中。如前所述,命令行属性总是优先于其它属性源。

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

24.3 应用程序属性文件

SpringApplication 从以下位置的 application.properties 文件加载属性,并将它们添加到 Spring Environment 中:

  1. 当前目录的 /config 子目录
  2. 当前目录
  3. 类路径 /config 包
  4. 类路径根目录

列表按优先级排序(在列表中较高的位置定义的属性重写那些在较低位置定义的属性)。

[Note] Note

你还可以 使用 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
[Warning] Warning

spring.config.name 和 spring.config.location 会被使用的非常早,来确定哪些文件需要被加载,所以它们必须定义成环境属性(通常是一个 OS 环境变量,一个系统属性,或一个命令行参数)。

如果 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(或者在 spring.config.name 中选择的其它基础名称)为应用程序提供默认值。这些默认值可以使用自定义位置中的不同文件在运行时被重写。

[Note] Note

如果你使用环境变量而不是系统属性,则大多数操作系统不允许句号分隔的键名,但可以使用下划线来替代(例如,SPRING_CONFIG_NAME 替代 spring.config.name)。

[Note] Note

如果你的应用程序在容器中运行,则可以使用 JNDI 属性(在 java:comp/env 中)或 servlet 上下文初始化参数来替代环境变量或系统属性。

24.4 特定配置组属性

除了 application.properties 之外,还可以使用以下命名约定来定义特定配置组属性:application-{profile}.properties。如果没有设置活动的特定配置组,Environment 中有一组默认配置组(默认情况下,[default])被使用。换句话说,如果没有特定配置组被显式激活,则加载 application-default.properties 的属性。

特定配置组属性从标准 application.properties 的相同位置加载。特定配置组文件总是重写非特定的,无论特定配置组文件是在打包的 jar 的内部还是外部。

如果指定了多个配置组,则应用最后一个胜利的策略。例如,spring.profiles.active 属性指定的配置组在 SpringApplication API 配置的属性之后添加,因此它们优先。

[Note] Note

如果你在 spring.config.location 中指定了一些文件,则不考虑这些特定配置组变体。如果希望使用特定配置组属性,则使用 spring.config.location 中的目录。

24.5 属性中的占位符

application.properties 中的属性值在使用时通过现有 Environment 进行筛选,因此可以引用先前定义的值(例如,从系统属性)。

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

你还可以使用此技术创建现有 Spring Boot 属性的短变体。更多详细信息请参阅 小节 75.4, “使用 ‘Short’ 命令行参数” how-to。

24.6 使用 YAML 替代属性文件

YAML 是一个 JSON 的超集,因此,它是一种用于指定分层属性数据的方便格式。当你的类路径上有 SnakeYAML 库时,SpringApplication 类会自动支持 YAML 作为属性文件的替代。

[Note] Note

如果你使用启动器,spring-boot-starter 会自动提供 SnakeYAML。

24.6.1 加载 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 utilities 那样绑定属性(这是 @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;
	}
}

24.6.2 在 Spring 环境中使用 YAML 作为属性文件

YamlPropertySourceLoader 类可用于将 YAML 作为 Spring Environment 的 PropertySource。这样做可以让你使用带有占位符语法的 @Value 注解来访问 YAML 属性。

24.6.3 多配置组 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。

[Note] Note

spring.profiles 可以包含一个简单的配置组名称(例如 production)或配置组表达式。一个配置组表达式允许更复杂的配置逻辑来表达,例如 production & (eu-central | eu-west)。更多详细信息请参阅参考指南

如果在应用程序上下文启动时没有显式激活,则激活默认配置组,因此,在下面的 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 元素指定的 Spring 配置组可以通过使用 ! 字符随时取消。如果单个文档指定了否定和非否定的配置组,则至少匹配一个非否定的配置组,并且可以匹配非否定的配置组。

24.6.4 YAML 缺陷

YAML 文件不能使用 @PropertySource 注解加载。因此,在需要以这种方式加载值的情况下,你需要使用属性文件。

24.7 类型安全配置属性

使用 @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 的集合。
[Note] Note

由于绑定是通过标准的 Java Bean 属性描述符,就像在 Spring MVC 中一样,getter 和 setter 通常是强制性的。在下列情况下,可以省略 setter:

  • 映射,只要它们被初始化,需要一个 getter,但不一定需要一个 setter,因为它们可以被绑定器进行改变。
  • 集合和数组可以通过索引(通常使用 YAML)或使用单个逗号分隔值(属性)来访问。在后一种情况下,setter 是强制性的。我们建议总是为这样的类型添加一个 setter。如果初始化集合,请确保它不是不可变的(如前面的示例)。
  • 如果初始化了嵌套 POJO 属性(如前面示例中的 Security 字段),则不需要 setter。如果希望绑定器使用它的默认构造函数即时创建实例,则需要一个 setter。

有些人使用 Lombok 项目自动添加 getter 和 setter。请确保 Lombok 不会为此类类型生成任何特定构造函数,因为容器自动使用该构造函数实例化对象。

最后,只有标准的 Java Bean 属性被考虑,并且不支持对静态属性的绑定。

[Tip] Tip

请参阅 @Value 和 @ConfigurationProperties 之间的不同.

你还需要列出属性类以注册在 @EnableConfigurationProperties 注解中,如下面的示例所示:

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

当以这种方式注册 @ConfigurationProperties bean 时,bean 有一个常规的名称:<prefix>-<fqn>,其中 <prefix> 是在 @ConfigurationProperties 注解中指定的环境关键前缀,而 <fqn> 是 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());
		// ...
	}

}
[Tip] Tip

使用 @ConfigurationProperties 也可以生成元数据,这些文件可以用于 IDE 为自己的关键字提供自动完成。更多详细信息请参阅 附录 B, 配置元数据 附录。

24.7.1 第三方配置

除了使用 @ConfigurationProperties 注解类之外,还可以在公共 @Bean 方法使用它。当你想将属性绑定到控件之外的第三方组件时,这样做可能特别有用。

若要从 Environment 属性配置 bean,请在其 bean 的注册中添加 @ConfigurationProperties,如下面的示例所示:

@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
	...
}

所有有 another 前缀的属性定义,以类似于前面的 AcmeProperties 示例的方式映射到 AnotherComponent bean。

24.7.2 松散绑定

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;
	}

}

在前面的示例中,可以使用以下属性名称:

表格 24.1. 松散的绑定

属性 备注

acme.my-project.person.first-name

串格式,建议在 .properties 和 .yml 文件中使用。

acme.myProject.person.firstName

标准的驼峰式语法

acme.my_project.person.first_name

下划线符号,它是 .properties 和 .yml 文件中使用的另一种格式。

ACME_MYPROJECT_PERSON_FIRSTNAME

大写格式,在使用系统环境变量时推荐。


[Note] Note

注解的前缀值必须是串格式(小写并使用 - 分隔,例如 acme.my-project.person)。

表格 24.2. 每个属性源的松散绑定规则

属性源 单个 列表

属性文件

驼峰式,串格式,或下划线符号

使用 [ ] 的标准列表语法或逗号分隔的值

YAML 文件

驼峰式,串格式,或下划线符号

标准 YAML 列表语法或逗号分隔的值

环境变量

用下划线作为定界符的大写格式。属性名内不应该使用 _

由下划线包围的数值,例如 MY_ACME_1_OTHER = my.acme[1].other

系统属性

驼峰式,串格式,或下划线符号

使用 [ ] 的标准列表语法或逗号分隔的值


[Tip] Tip

我们建议,在可能的情况下,属性以小写的串格式存储,例如 my.property-name=acme。

当绑定到 Map 属性时,如果 key 包含除小写字母数字字符或 - 以外的任何字符,则需要使用括号表示法,以便保留原始值。如果 key 未被 [] 包围,则任何非字母数字或 - 的字符都被删除。例如,考虑将下列属性绑定到 Map:

acme:
  map:
    "[/key1]": value1
    "[/key2]": value2
    /key3: value3

上面的属性使用 /key1、/key2 和 key3 为 map 中的键来绑定到 Map。

24.7.3 合并复杂类型

当列表在多个位置配置时,通过替换整个列表来重写。

例如,假设 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 配置组是启用的,列表仍然只包含一个记录(name为 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

在前面的示例中,如果 dev 配置组是激活的,AcmeProperties.list 包含一个 MyPojo 记录(name为 my another name,description 为 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

如果 dev 配置组不是激活的,AcmeProperties.map 包含一个 key1 关键字的记录(name 是 my name 1, description 是 my description 1)。如果 dev 配置组是启用的,那么,map 包含 key1 关键字(name 是 dev name 1, description 是 my description 1)和 key2 关键字(name 是 dev name 2, description 是 dev description 2)的两个记录

[Note] Note

前面的合并规则适用于所有属性源的属性,而不只是 YAML 文件。

24.7.4 属性转换

Spring Boot 在绑定到 @ConfigurationProperties bean 时试图将外部应用程序属性强制转换为恰当的类型。如果你需要自定义类型转换,可以提供一个 ConversionService bean(一个命名为 conversionService 的 bean)或自定义属性编辑器(通过一个 CustomEditorConfigurer bean)或自定义 Converters(定义为 @ConfigurationPropertiesBinding 的 bean)。

[Note] Note

由于这个 bean 在应用程序生命周期中被请求得很早,请务必限制 ConversionService 使用的依赖关系。通常,你需要的一些依赖项在创建时可能未完全初始化。如果不需要配置键强制,则只需要重命名自定义 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 当作 nanoseconds(纳秒)
  • us 当作 microseconds(微秒)
  • ms 当作 milliseconds(毫秒)
  • s 当作 seconds(秒)
  • m 当作 minutes(分钟)
  • h 当作 hours(小时)
  • d 当作 days(天)

默认的单位是毫秒,可以用上面的示例中所示的 @DurationUnit 来重写。

[Tip] Tip

如果你是从只使用 Long 来表示持续时间的版本升级,那么如果在转换到 Duration 的时候不确定是毫秒,请确保定义单位(使用 @DurationUnit)。这样做提供了透明的升级路径,同时支持更丰富的格式。

转换数据大小

Spring Framework 具有一个 DataSize 值类型,以用来表示字节的大小 。如果暴露 DataSize 属性,应用程序属性中的以下格式可用:

  • 规则 long 表示(除非指定了@DataSizeUnit,否则使用字节作为默认单元 )
  • 一个更可读的格式,其中的值和单位是耦合的(例如 10MB 意味着 10兆字节)。

参考下面的例子:

@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兆字节的缓冲区大小,10 和 10MB 是等效的。256字节的大小阈值可以指定为 256 或 256B。

你也可以使用任何支持的单位。它们是:

  • B 当作 bytes(字节)
  • KB 当作 kilobytes(千字节)
  • MB 当作 megabytes(兆字节)
  • GB 当作 gigabytes(千兆字节)
  • TB 当作 terabytes(兆兆字节)

默认的单位是字节,可以用上面的示例中所示的 @DataSizeUnit 来重写。

[Tip] Tip

如果你是从只使用 Long 来表示大小的版本升级,那么如果在转换到 DataSize 的时候不确定是字节,请确保定义单位(使用 @DataSizeUnit)。这样做提供了透明的升级路径,同时支持更丰富的格式。

24.7.5 @ConfigurationProperties 校验

Spring Boot 会尝试校验使用 Spring 的 @Validated 注解来注解的 @ConfigurationProperties 类。你可以直接在配置类上使用 JSR-303 javax.validation 约束注解。为此,请确保兼容的 JSR-303 实现在类路径上,然后将约束注解添加到字段中,如下面的示例所示:

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

	@NotNull
	private InetAddress remoteAddress;

	// ... getters and setters

}
[Tip] Tip

你还可以通过注解 @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 方法声明为静态,可以在不必实例化 @Configuration 的情况下创建 bean。这样做避免了早期实例化可能引起的任何问题。这有一个属性校验示例演示如何设置。

[Tip] Tip

spring-boot-actuator 模块包含一个暴露所有 @ConfigurationProperties bean 的端点。将 Web 浏览器指向 /actuator/configprops 或使用等效的 JMX 端点。详情见 "s生产就绪特性" 部分。

24.7.6 @ConfigurationProperties vs. @Value

@Value 注解是一个核心容器特性,它不提供与类型安全配置属性相同的特性。下表总结了 @ConfigurationProperties 和 @Value 支持的特性:

特性 @ConfigurationProperties @Value

松散绑定

Yes

No

元数据支持

Yes

No

SpEL 计算

No

Yes

如果你为自己的组件定义了一组配置键,建议将它们在带有 @ConfigurationProperties 注解的 POJO 中分组。你还应该意识到,因为 @Value 不支持松散绑定,所以如果需要使用环境变量提供值,它则不是一个好的候选。

最后,虽然可以在 @Value 中写入 SpEL 表达式,但这些表达式不会从应用程序属性文件中处理。

25. 配置组

Spring 配置组提供了一种隔离应用程序配置的部分并使其仅在某些环境下可用的方法。任何 @Component 或 @Configuration 都可以标记为 @Profile 来限制它们的加载,如下面的示例所示:

@Configuration
@Profile("production")
public class ProductionConfiguration {

	// ...

}

你可以使用 spring.profiles.active Environment 属性来指定哪个配置组是活动的。你可以通过本章前面描述的任何方式来指定属性。例如,可以将其包含在 application.properties 中,如下面的示例所示:

spring.profiles.active=dev,hsqldb

还可以使用以下开关在命令行上指定它:--spring.profiles.active=dev,hsqldb。

25.1 添加激活的配置组

spring.profiles.active 属性遵循与其它属性相同的排序规则:优先级最高的 PropertySource 获胜。这意味着可以在 application.properties 中指定激活的配置组,然后使用命令行开关替换它们。

有时,将特定配置组的属性添加到激活的配置组中而不是替换它们是有好处的。spring.profiles.include 属性可以用来无条件的添加激活的配置组。SpringApplication 入口点也提供了一个 Java API 来设置附加的配置组(也就是说,在 spring.profiles.active 属性激活的那些组之上)。请参阅 SpringApplication 中的 setAdditionalProfiles() 方法。

例如,当一个应用使用下面的属性,并用 --spring.profiles.active=prod 开关运行时,proddb 和 prodmq 配置文件也会被激活:

---
my.property: fromyamlfile
---
spring.profiles: prod
spring.profiles.include:
  - proddb
  - prodmq
[Note] Note

请记住,spring.profiles 属性可以在 YAML 文件中定义,以确定该特定文件何时包含在配置中。详情请参阅 小节 76.7, “根据环境改变配置”。

25.2 编程方式设置配置组

你可以通过在应用程序运行之前调用 SpringApplication.setAdditionalProfiles(…​) 来以编程的方式设置激活的配置组。还可以通过使用 Spring 的 ConfigurableEnvironment 接口激活配置组。

25.3 特定配置组的配置文件

application.properties(或 application.yml)和通过 @ConfigurationProperties 引用的文件的特定配置组变体被视为文件并加载。详细信息请参阅 小节 24.4, “特定配置组属性”。