24. Externalized Configuration

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

Spring Boot 使用一个非常特殊的PropertySourceSequences,该 Sequences 被设计为允许明智地覆盖值。按以下 Sequences 考虑属性:

  • 您的主目录上的Devtools 全局设置属性(在 devtools 处于 Active 状态时为~/.spring-boot-devtools.properties)。

  • @TestPropertySource您的测试 Comments。

  • 测试中的properties属性。在@SpringBootTest测试 Comments 以测试应用程序的特定部分上可用。

  • 命令行参数。

  • SPRING_APPLICATION_JSON中的属性(嵌入在环境变量或系统属性中的内联 JSON)。

  • ServletConfig个初始化参数。

  • ServletContext个初始化参数。

  • 来自java:comp/env的 JNDI 属性。

  • Java 系统属性(System.getProperties())。

  • os 环境变量。

  • 仅在random.*中具有属性的RandomValuePropertySource

  • 特定于配置文件的应用程序属性在打包的 jar 之外(application-{profile}.properties和 YAML 变体)。

  • 特定于配置文件的应用程序属性包装在 jar 中(application-{profile}.properties和 YAML 变体)。

  • 打包的 jar 之外的应用程序属性(application.properties和 YAML 变体)。

  • 打包在 jar 中的应用程序属性(application.properties和 YAML 变体)。

  • @Configuration个类上的@PropertySource条 Comments。

  • 默认属性(通过设置SpringApplication.setDefaultProperties指定)。

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

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

@Component
public class MyBean {

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

    // ...

}

在您的应用程序 Classpath 上(例如,在 jar 内),您可以拥有一个application.properties文件,该文件为name提供了合理的默认属性值。在新环境中运行时,可以在 jar 外部提供application.properties文件,该文件将覆盖name。对于一次性测试,可以使用特定的命令行开关(例如java -jar app.jar --name="Spring")启动。

Tip

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

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

在前面的示例中,您在 SpringEnvironment中以acme.name=test结尾。您还可以在 System 属性中以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可用于注入随机值(例如,Importing 到机密或测试用例中)。它可以产生整数,longs,uuid 或字符串,如以下示例所示:

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是最大值(不包括)。

24.2 访问命令行属性

默认情况下,SpringApplication将任何命令行选项参数(即以--开头的参数,例如--server.port=9000)转换为property并将其添加到 Spring Environment。如前所述,命令行属性始终优先于其他属性源。

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

24.3 应用程序属性文件

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

  • 当前目录的/config子目录

  • 当前目录

  • Classpath/config

  • Classpath 根

该列表按优先级排序(在列表较高位置定义的属性会覆盖在较低位置定义的属性)。

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

spring.config.namespring.config.location很早就用于确定必须加载哪些文件,因此必须将它们定义为环境属性(通常是 OS 环境变量,系统属性或命令行参数)。

如果spring.config.location包含目录(而不是文件),则它们应以/结尾(并且在运行时,应在加载之前附加从spring.config.name生成的名称,包括特定于配置文件的文件名)。 spring.config.location中指定的文件按原样使用,不支持特定于配置文件的变体,并且被任何特定于配置文件的属性覆盖。

配置位置以相反的 Sequences 搜索。默认情况下,配置的位置是classpath:/,classpath:/config/,file:./,file:./config/。结果搜索 Sequences 如下:

  • file:./config/

  • file:./

  • classpath:/config/

  • classpath:/

使用spring.config.location配置自定义配置位置后,它们将替换默认位置。例如,如果将spring.config.location配置为值classpath:/custom-config/,file:./custom-config/,则搜索 Sequences 如下:

  • file:./custom-config/

  • classpath:custom-config/

或者,当使用spring.config.additional-location配置自定义配置位置时,除默认位置外还使用它们。在默认位置之前搜索其他位置。例如,如果配置了classpath:/custom-config/,file:./custom-config/的其他位置,则搜索 Sequences 如下:

  • file:./custom-config/

  • classpath:custom-config/

  • file:./config/

  • file:./

  • classpath:/config/

  • classpath:/

通过此搜索 Sequences,您可以在一个配置文件中指定默认值,然后在另一个配置文件中有选择地覆盖这些值。您可以在默认位置之一的application.properties(或使用spring.config.name选择的其他任何基本名称)中为应用程序提供默认值。然后,可以在运行时使用自定义位置之一中的其他文件覆盖这些默认值。

Note

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

Note

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

24.4 特定于配置文件的属性

除了application.properties个文件之外,还可以使用以下命名约定来定义特定于配置文件的属性:application-{profile}.propertiesEnvironment具有一组默认配置文件(默认情况下为[default]),如果未设置任何 Active 配置文件,则使用这些配置文件。换句话说,如果未显式激活任何配置文件,则将加载application-default.properties中的属性。

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

如果指定了多个配置文件,则采用后赢策略。例如,由spring.profiles.active属性指定的配置文件将添加到通过SpringApplication API 配置的配置文件之后,因此具有优先权。

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

您还可以使用此技术来创建现有 Spring Boot 属性的“简短”变体。有关详细信息,请参见* 第 77.4 节“使用'短'命令行参数” *操作方法。

24.6 加密属性

Spring Boot 不提供对加密属性值的任何内置支持,但是,它确实提供了修改 Spring Environment中包含的值所必需的钩子点。 EnvironmentPostProcessor界面允许您在应用程序启动之前操纵Environment。有关详情,请参见第 76.3 节“在启动前自定义环境或 ApplicationContext”

如果您正在寻找一种安全的方式来存储凭据和密码,则Spring Cloud Vault项目提供了对将外部化配置存储在HashiCorp Vault中的支持。

24.7 使用 YAML 代替属性

YAML是 JSON 的超集,因此是一种用于指定层次结构配置数据的便捷格式。只要在 Classpath 上具有SnakeYAML库,SpringApplication类就会自动支持 YAML 作为属性的替代方法。

Note

如果您使用“Starter”,则spring-boot-starter自动提供 SnakeYAML。

24.7.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 的BinderUtil(即@ConfigurationProperties所做的)绑定到类似的属性,您需要在java.util.List(或Set)类型的目标 bean 中具有一个属性,并且需要提供 setter 或使用 a 初始化它。可变值。例如,以下示例绑定到前面显示的属性:

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

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

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

24.7.2 在 Spring 环境中将 YAML 公开为属性

YamlPropertySourceLoader类可用于在 Spring Environment中将 YAML 公开为PropertySource。这样做使您可以将@ValueComments 与占位符语法一起使用以访问 YAML 属性。

24.7.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配置文件处于 Active 状态,则server.address属性为127.0.0.1。同样,如果productioneu-central配置文件处于 Active 状态,则server.address属性为192.168.1.120。如果developmentproductioneu-central配置文件未启用,则该属性的值为192.168.1.100

Note

因此spring.profiles可以包含一个简单的配置文件名称(例如production)或一个配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如production & (eu-central | eu-west)。查看reference guide以获取更多详细信息。

如果在启动应用程序上下文时未明确激活任何 Active,则会激活默认配置文件。因此,在以下 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.7.4 YAML 缺点

无法使用@PropertySourceComments 加载 YAML 文件。因此,在需要以这种方式加载值的情况下,需要使用属性文件。

24.8 类型安全的配置属性

使用@Value("${property}")注解注入配置属性有时会很麻烦,尤其是当您使用多个属性或数据本质上是分层的时。 Spring Boot 提供了一种使用属性的替代方法,该方法使强类型的 Bean 可以 Management 和验证应用程序的配置,如以下示例所示:

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,带有嵌套的“安全”对象,其名称由属性名称确定。特别是,返回类型在那里根本不使用,可能是SecurityProperties

  • acme.security.password .

  • acme.security.roles,集合为String

Note

Getter 和 Setter 通常是强制性的,因为绑定是通过标准 Java Beans 属性 Descriptors 进行的,就像在 Spring MVC 中一样。在以下情况下,可以忽略二传手:

  • 只要将 Map 初始化,它们就需要使用吸气剂,但不一定需要使用 setter,因为它们可以被 Binder 改变。

  • 可以通过索引(通常使用 YAML)或使用单个逗号分隔的值(属性)来访问集合和数组。在后一种情况下,必须使用二传手。我们建议始终为此类类型添加设置器。如果初始化集合,请确保它不是不可变的(如上例所示)。

  • 如果初始化嵌套的 POJO 属性(如上例中的Security字段),则不需要设置器。如果希望 Binder 通过使用其默认构造函数动态创建实例,则需要一个 setter。

有些人使用 Lombok 项目自动添加获取器和设置器。确保 Lombok 不会为这种类型生成任何特定的构造函数,因为容器会自动使用它来实例化该对象。

最后,仅考虑标准 Java Bean 属性,不支持对静态属性的绑定。

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

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

Note

以这种方式注册@ConfigurationProperties bean 时,该 bean 具有常规名称:<prefix>-<fqn>,其中<prefix>@ConfigurationProperties注解中指定的环境密钥前缀,而<fqn>是 bean 的全限定名。如果 Comments 不提供任何前缀,则仅使用 Bean 的完全限定名称。

上例中的 bean 名称是acme-com.example.AcmeProperties

即使前面的配置为AcmeProperties创建了一个常规 bean,我们也建议@ConfigurationProperties只处理环境,尤其不要从上下文中注入其他 bean。话虽如此,@EnableConfigurationProperties注解也会自动应用到您的项目中,从而可以从Environment配置任何以@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

使用@ConfigurationProperties还可让您生成元数据文件,IDE 可以使用这些元数据文件为您自己的键提供自动完成功能。有关详细信息,请参见附录 B,配置元数据附录。

24.8.1 第三方配置

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

要通过Environment属性配置 bean,请将@ConfigurationProperties添加到其 bean 注册中,如以下示例所示:

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

another前缀定义的任何属性都以类似于前面AcmeProperties示例的方式 Map 到该AnotherComponent bean。

24.8.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. 轻松绑定

PropertyNote
acme.my-project.person.first-nameKebab 情况,建议在.properties.yml文件中使用。
acme.myProject.person.firstName标准驼峰式语法。
acme.my_project.person.first_name下划线表示法,是在.properties.yml文件中使用的另一种格式。
ACME_MYPROJECT_PERSON_FIRSTNAME大写格式,使用系统环境变量时建议使用。

Note

Comments*的prefix值必须为 kebab(小写并由-分隔,例如acme.my-project.person)。

表 24.2. 每个资源来源的宽松绑定规则

Property SourceSimpleList
Properties Files骆驼案,烤肉串案或下划线使用[ ]或逗号分隔值的标准列表语法
YAML Files骆驼案,烤肉串案或下划线标准 YAML 列表语法或逗号分隔的值
Environment Variables以下划线作为定界符的大写格式。 _不应在属性名称中使用带有下划线的数字值,例如MY_ACME_1_OTHER = my.acme[1].other
System properties骆驼案,烤肉串案或下划线使用[ ]或逗号分隔值的标准列表语法

Tip

我们建议,如果可能的话,属性以小写的 kebab 格式存储,例如my.property-name=acme

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

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

上面的属性将绑定到Map,其中/key1/key2key3作为 Map 中的键。

24.8.3 合并复杂类型

如果在多个位置配置了列表,则通过替换整个列表来进行覆盖。

例如,假设一个MyPojo对象的namedescription属性默认为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配置文件,则list 仍然仅包含一个条目(名称为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配置文件处于 Active 状态,则AcmeProperties.list包含* one * MyPojo条目(名称为my another name且描述为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的条目(名称为my name 1和描述为my description 1)。但是,如果启用了dev配置文件,则map包含两个条目,其中包含键key1(名称为dev name 1my description 1的描述)和key2(名称为dev name 2dev description 2的描述)。

Note

前述合并规则不仅适用于 YAML 文件,而且适用于所有属性源中的属性。

24.8.4 属性转换

当 Spring Boot 绑定到@ConfigurationProperties bean 时,它尝试将外部应用程序属性强制为正确的类型。如果需要自定义类型转换,则可以提供ConversionService bean(使用名为conversionService的 bean)或自定义属性编辑器(通过CustomEditorConfigurer bean)或自定义Converters(将 bean 定义标注为@ConfigurationPropertiesBinding)。

Note

由于在应用程序生命周期中非常早就请求了此 bean,因此请确保限制您的ConversionService使用的依赖项。通常,您需要的任何依赖项可能在创建时未完全初始化。如果配置键强制不需要自定义ConversionService,而只依赖于具有@ConfigurationPropertiesBinding限定的自定义转换器,则可能要重命名。

Converting durations

Spring Boot 为表达持续时间提供了专门的支持。如果公开java.time.Duration属性,则应用程序属性中的以下格式可用:

  • 常规long表示形式(使用毫秒作为默认单位,除非已指定@DurationUnit)

  • 标准 ISO-8601 格式由 java.util.Duration 使用

  • 值和单位相结合的更易读的格式(例如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 秒的会话超时,30PT30S30s都是等效的。可以采用以下任何形式指定 500ms 的读取超时:500PT0.5S500ms

您也可以使用任何受支持的单位。这些是:

  • ns纳秒

  • us微秒

  • ms毫秒

  • s

  • m分钟

  • h小时

  • d

默认单位是毫秒,可以使用@DurationUnit覆盖,如上面的示例所示。

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 MB 的缓冲区大小,1010MB是等效的。可以将 256 个字节的大小阈值指定为256256B

您也可以使用任何受支持的单位。这些是:

  • B个字节

  • KB千字节

  • MB代表兆字节

  • GB代表千兆字节

  • TB代表兆兆字节

默认单位是字节,可以使用@DataSizeUnit覆盖,如上面的示例所示。

Tip

如果要从仅使用Long表示大小的先前版本进行升级,请确保在切换到DataSize的旁边不是字节的情况下定义单位(使用@DataSizeUnit)。这样做可以提供透明的升级路径,同时支持更丰富的格式。

24.8.5 @ConfigurationProperties 验证

每当使用 Spring 的@ValidatedComments 进行 Comments 时,Spring Boot 就会尝试验证@ConfigurationProperties类。您可以在配置类上直接使用 JSR-303 javax.validation约束 Comments。为此,请确保在 Classpath 上有兼容的 JSR-303 实现,然后将约束 Comments 添加到字段中,如以下示例所示:

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

	@NotNull
	private InetAddress remoteAddress;

	// ... getters and setters

}

Tip

您还可以通过使用@ValidatedComments 创建配置属性的@Bean方法来触发验证。

尽管嵌套属性在绑定时也会被验证,但是最好将关联的字段 Comments 为@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类。这样做可以避免因早期实例化而引起的任何问题。有一个属性验证 samples显示了如何进行设置。

Tip

spring-boot-actuator模块包含一个公开所有@ConfigurationProperties bean 的端点。将您的 Web 浏览器指向/actuator/configprops或使用等效的 JMX 端点。有关详细信息,请参见“ 生产就绪功能”部分。

24.8.6 @ConfigurationProperties 与@Value

@Value注解是核心容器功能,它没有提供与类型安全的配置属性相同的功能。下表总结了@ConfigurationProperties@Value支持的功能:

Feature@ConfigurationProperties@Value
Relaxed bindingYesNo
Meta-data supportYesNo
SpEL评估NoYes

如果您为自己的组件定义了一组配置键,我们建议您将它们组合在以@ConfigurationPropertiesComments 的 POJO 中。您还应该意识到,由于@Value不支持宽松的绑定,因此如果您需要使用环境变量来提供值,则它不是很好的选择。

最后,尽管您可以在@Value中编写SpEL表达式,但不会从应用程序属性文件处理此类表达式。