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

灯泡.svg 另请参阅@Value和类型安全配置属性的区别

2.7.1 JavaBean属性绑定

可以绑定一个声明标准JavaBean属性的bean,如以下示例所示:

  1. package com.example;
  2. import java.net.InetAddress;
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.List;
  6. import org.springframework.boot.context.properties.ConfigurationProperties;
  7. @ConfigurationProperties("acme")
  8. public class AcmeProperties {
  9. private boolean enabled;
  10. private InetAddress remoteAddress;
  11. private final Security security = new Security();
  12. public boolean isEnabled() { ... }
  13. public void setEnabled(boolean enabled) { ... }
  14. public InetAddress getRemoteAddress() { ... }
  15. public void setRemoteAddress(InetAddress remoteAddress) { ... }
  16. public Security getSecurity() { ... }
  17. public static class Security {
  18. private String username;
  19. private String password;
  20. private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
  21. public String getUsername() { ... }
  22. public void setUsername(String username) { ... }
  23. public String getPassword() { ... }
  24. public void setPassword(String password) { ... }
  25. public List<String> getRoles() { ... }
  26. public void setRoles(List<String> roles) { ... }
  27. }
  28. }

前面的POJO(Plain Ordinary Java Object)定义了以下属性:

  • acme.enabled,其默认值为false
  • acme.remote-address,其类型可以从String强制转换。
  • acme.security.username,带有嵌套的“ security(安全)”对象,其名称由属性名称确定。 特别是,返回类型根本不使用,可能是SecurityProperties(安全属性)。
  • acme.security.password,与上面的类似。
  • acme.security.roles,带有默认为USER的一个String类型的集合。

info.svg Spring Boot中,映射到@ConfigurationProperties类的属性(这些属性是通过属性文件,YAML文件,环境变量等配置的)是公共的API,但是类本身的访问器(getters/setters)并不意味着可以直接使用。

info.svg 这种安排依赖于默认的空构造函数,并且getter和setter通常是强制性的,因为绑定是通过标准Java Beans属性描述符进行的,就像在Spring MVC中一样。在以下情况中,可以省略setter:

  • Maps(映射),只要它们被始化,就只需要使用getter而不需要使用setter,因为它们可以通过绑定器(被绑定字段)来改变。
  • 既可以通过索引(通常是YAML),也可以使用单个逗号分隔的值(通常是Properties)来访问集合和数组。在后一种情况下,必须使用setter。我们建议始终为此类类型添加setter。如果初始化集合,请确保它是可变的(如上例所示的是不可变的)。
  • 如果嵌套的POJO属性已被初始化(如前面示例中的Security字段),则不需要setter方法。如果希望绑定器(被绑定字段)通过使用其默认构造函数动态创建实例,则需要一个setter。

有些人使用Lombok项目自动添加setter和getter。确保Lombok不会为这种类型生成任何特定的构造函数,因为容器会自动使用它来实例化该对象。 最后,仅考虑标准Java Bean属性,不支持对静态属性的绑定。

2.7.2 构造函数绑定

上一节中的示例可以以不可变(字段)的方式重写,如下例所示:

  1. package com.example;
  2. import java.net.InetAddress;
  3. import java.util.List;
  4. import org.springframework.boot.context.properties.ConfigurationProperties;
  5. import org.springframework.boot.context.properties.ConstructorBinding;
  6. import org.springframework.boot.context.properties.bind.DefaultValue;
  7. @ConstructorBinding
  8. @ConfigurationProperties("acme")
  9. public class AcmeProperties {
  10. private final boolean enabled;
  11. private final InetAddress remoteAddress;
  12. private final Security security;
  13. public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
  14. this.enabled = enabled;
  15. this.remoteAddress = remoteAddress;
  16. this.security = security;
  17. }
  18. public boolean isEnabled() { ... }
  19. public InetAddress getRemoteAddress() { ... }
  20. public Security getSecurity() { ... }
  21. public static class Security {
  22. private final String username;
  23. private final String password;
  24. private final List<String> roles;
  25. public Security(String username, String password,
  26. @DefaultValue("USER") List<String> roles) {
  27. this.username = username;
  28. this.password = password;
  29. this.roles = roles;
  30. }
  31. public String getUsername() { ... }
  32. public String getPassword() { ... }
  33. public List<String> getRoles() { ... }
  34. }
  35. }

在此设置中,@ConstructorBinding注解用于指示应使用构造函数绑定。这意味着绑定器(被绑定字段)将期望找到带有您希望绑定的参数的构造函数。
@ConstructorBinding类的嵌套成员(例如,上例中的Security)也将通过其构造函数进行绑定。
可以使用@DefaultValue指定默认值,并将应用相同的转换服务将String值强制转化为缺失属性的目标类型。默认情况下,如果未将任何属性绑定到Security,则AcmeProperties实例包含的security字段将为null。如果您希望Security即使没有属性绑定也返回一个非null的实例,则可以使用一个空的@DefaultValue注解达到这种效果:

  1. package com.example;
  2. import java.net.InetAddress;
  3. import java.util.List;
  4. import org.springframework.boot.context.properties.ConfigurationProperties;
  5. import org.springframework.boot.context.properties.ConstructorBinding;
  6. import org.springframework.boot.context.properties.bind.DefaultValue;
  7. @ConstructorBinding
  8. @ConfigurationProperties("acme")
  9. public class AcmeProperties {
  10. private final boolean enabled;
  11. private final InetAddress remoteAddress;
  12. private final Security security;
  13. public AcmeProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
  14. this.enabled = enabled;
  15. this.remoteAddress = remoteAddress;
  16. this.security = security;
  17. }
  18. }

info.svg 要使用构造函数绑定,必须使用@EnableConfigurationProperties或配置属性扫描来启用该类。您不能对通过常规Spring机制创建的bean(例如,@Componentbean、通过@Bean方法创建的bean或使用@Import加载的bean )使用构造函数绑定。

灯泡.svg 如果您的类具有多个构造函数,则可以直接在应绑定的构造函数上使用@ConstructorBinding

info.svg 不建议java.util.Optional@ConfigurationProperties一起使用,因为它主要用于返回类型。因此,它不太适合配置属性注入。为了与其他类型的属性保持一致,如果确实声明了一个Optional属性且该属性没有值,则将会绑定成null,而不是一个空的Optional

2.7.3 启用@ConfigurationProperties注释的类型

Spring Boot提供了绑定@ConfigurationProperties类型并将其注册为Bean的基础架构。您可以逐类启用配置属性,也可以启用与组件扫描类似的方式进行配置属性扫描。
有时,带@ConfigurationProperties注解的类可能不适合扫描,例如,如果您正在开发自己的自动配置,或者想要有条件地启用它们。在这些情况下,请使用@EnableConfigurationProperties注解指定要处理的类型列表。可以在任何@Configuration类上完成此操作,如以下示例所示:

  1. @Configuration(proxyBeanMethods = false)
  2. @EnableConfigurationProperties(AcmeProperties.class)
  3. public class MyConfiguration {
  4. }

要使用配置属性扫描,请将@ConfigurationPropertiesScan注解添加到您的应用程序。通常,它将添加到带有@SpringBootApplication注解的主应用程序类中,但也可以将其添加到任何@Configuration类中。默认情况下,将从声明注释的类所在的包中进行扫描。如果要定义要扫描的特定程序包,可以按照以下示例所示进行操作:

  1. @SpringBootApplication
  2. @ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
  3. public class MyApplication {
  4. }

info.svg 当使用配置属性扫描或通过@EnableConfigurationProperties注册@ConfigurationPropertiesbean,这个bean有个常规的名称:<prefix>-<fqn>。其中,<prefix>是在@ConfigurationProperties注解中指定的环境键的前缀,<fqn>是bean的完全限定名。如果@ConfigurationProperties注解不提供任何前缀,则仅使用bean的完全限定名称。 上例中的bean名称为acme-com.example.AcmeProperties

我们建议@ConfigurationProperties仅用于处理环境,尤其不要从上下文中注入其他bean。对于极端情况,可以使用setter注入,或者使用框架提供的任何*Aware接口(例如,如果需要访问Environment,则使用EnvironmentAware)。如果仍然要使用构造函数注入其他bean,则必须使用@Component注解此配置属性bean,并使用基于JavaBean的属性绑定。

2.7.4 使用@ConfigurationProperties注解的类型

这种配置样式特别适用于SpringApplication的外部YAML配置,如下所示:

  1. acme:
  2. remote-address: 192.168.1.1
  3. security:
  4. username: admin
  5. roles:
  6. - USER
  7. - ADMIN

要使用@ConfigurationPropertiesbean,可以像使用其他任何bean一样注入它们,如下所示:

  1. @Service
  2. public class MyService {
  3. private final AcmeProperties properties;
  4. @Autowired
  5. public MyService(AcmeProperties properties) {
  6. this.properties = properties;
  7. }
  8. //...
  9. @PostConstruct
  10. public void openConnection() {
  11. Server server = new Server(this.properties.getRemoteAddress());
  12. // ...
  13. }
  14. }

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

2.7.5 第三方配置

@ConfigurationProperties除了可以用于注解类外,还可以在public的@Bean方法上使用。当要将属性绑定到控制范围之外的第三方组件时,这样做特别有用。
要从Environment属性中配置bean ,添加@ConfigurationProperties到它的bean注册中,如下示例所示:

  1. @ConfigurationProperties(prefix = "another")
  2. @Bean
  3. public AnotherComponent anotherComponent() {
  4. ...
  5. }

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

2.7.6 宽松绑定

Spring Boot使用一些宽松的规则将Environment属性绑定到@ConfigurationPropertiesBean,因此Environment属性名称和Bean属性名称之间不需要完全匹配。有用的常见示例包括:破折号分隔的环境属性(例如,context-path绑定到contextPath),大写的环境属性(例如,PORT绑定到port)。
例如,考虑如下的@ConfigurationProperties类:

  1. @ConfigurationProperties(prefix="acme.my-project.person")
  2. public class OwnerProperties {
  3. private String firstName;
  4. public String getFirstName() {
  5. return this.firstName;
  6. }
  7. public void setFirstName(String firstName) {
  8. this.firstName = firstName;
  9. }
  10. }

前面的代码中,可以使用以下属性名称:
表3.宽松的绑定

属性 注意
acme.my-project.person.first-name 短横线命名(Kebab case),在.properties.yml文件中,建议使用。
acme.myProject.person.firstName 标准驼峰式语法。
acme.my_project.person.first_name 下划线表示法,在.properties.yml文件中,建议使用的另一种格式。
ACME_MYPROJECT_PERSON_FIRSTNAME 大写格式,在使用系统环境变量时,建议这样使用。

灯泡.svg prefix注解的值必须使用短横线命名(用-分隔的小写字母,例如acme.my-project.person)。

表4.每个属性源的宽松绑定规则

属性源 单个 列表
Properties文件 骆驼命名法,短横线命名法,下划线分隔 使用[ ]或以逗号分隔值的标准列表语法
YAML文件 骆驼命名法,短横线命名法,下划线分隔 标准YAML列表语法或以逗号分隔的值
环境变量 以下划线作为定界符的大写字母格式(请参阅从环境变量绑定)。 包围的数字值的下划线界定格式(请参阅环境变量的绑定
系统属性 骆驼命名法,短横线命名法,下划线分隔 使用[ ]或以逗号分隔值的标准列表语法

灯泡.svg 我们建议,如果可能的话,属性以小写的以短横线间隔的格式存储,例如my.property-name=acme

绑定映射

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

  1. acme.map.[/key1]=value1
  2. acme.map.[/key2]=value2
  3. acme.map./key3=value3

Yaml:

  1. acme:
  2. map:
  3. "[/key1]": value1
  4. "[/key2]": value2
  5. "/key3": value3

上面的属性绑定到Map/key1/key2key3的键。

info.svg 对于YAML文件,方括号需要用引号引起来,以键能被正确解析。

环境变量的绑定

大多数操作系统对可用于环境变量的名称有严格的规则。例如,Linux shell变量只能包含字母(azAZ)、数字(09)或下划线字符(_)。按照约定,Unix shell变量也将以大写字母命名。
Spring Boot的宽松绑定规则尽可能设计成与这些命名限制相兼容。
要将规范形式的属性名称转换为环境变量名称,可以遵循以下规则:

  • 用下划线(_)代替点(.)。
  • 删除所有破折号(-)。
  • 转换成大写。

例如,配置属性spring.main.log-startup-info是名为SPRING_MAIN_LOGSTARTUPINFO的环境变量。
绑定到对象列表时也可以使用环境变量。要绑定到List,元素编号应在变量名称中用下划线包起来。
例如,配置属性my.acme[0].other将使用名为MY_ACME_0_OTHER的环境变量。

2.7.7 合并复杂类型

如果在多个位置配置了列表,则通过替换整个列表来进行覆盖。
例如,假定MyPojo具有namedescription属性,默认情况下属性值为null。以下示例暴露了来自AcmeProperties对象的MyPojo列表:

  1. @ConfigurationProperties("acme")
  2. public class AcmeProperties {
  3. private final List<MyPojo> list = new ArrayList<>();
  4. public List<MyPojo> getList() {
  5. return this.list;
  6. }
  7. }

考虑以下配置:
Properties:

  1. acme.list[0].name=my name
  2. acme.list[0].description=my description
  3. #---
  4. spring.config.activate.on-profile=dev
  5. acme.list[0].name=my another name

Yaml:

  1. acme:
  2. list:
  3. - name: "my name"
  4. description: "my description"
  5. ---
  6. spring:
  7. config:
  8. activate:
  9. on-profile: "dev"
  10. acme:
  11. list:
  12. - name: "my another name"

如果dev配置未激活,则AcmeProperties.list包含一个MyPojo条目,如前面定义的那样。如果启用了dev配置,则list 仍然仅包含一个条目(name为my another name,description为null)。此配置不会在列表中添加第二个MyPojo实例,并且不会合并项目。
在多个配置中指定了List时,将使用优先级最高的那个(并且只是该优先级的那个)。考虑以下示例:
Properties:

  1. acme.list[0].name=my name
  2. acme.list[0].description=my description
  3. acme.list[1].name=another name
  4. acme.list[1].description=another description
  5. #---
  6. spring.config.activate.on-profile=dev
  7. acme.list[0].name=my another name

Yaml:

  1. acme:
  2. list:
  3. - name: "my name"
  4. description: "my description"
  5. - name: "another name"
  6. description: "another description"
  7. ---
  8. spring:
  9. config:
  10. activate:
  11. on-profile: "dev"
  12. acme:
  13. list:
  14. - name: "my another name"

在前面的示例中,如果dev配置处于活动状态,则AcmeProperties.list包含一个 MyPojo条目(name为my another name,description为null)。对于YAML而言,可以使用逗号分隔的列表和YAML列表都可以完全覆盖列表的内容。

对于Map属性,可以绑定从多个来源的属性值。但是,对于多个源中的同一属性,将使用优先级最高的属性。以下示例暴露了AcmeProperties中的Map<String, MyPojo>

  1. @ConfigurationProperties("acme")
  2. public class AcmeProperties {
  3. private final Map<String, MyPojo> map = new HashMap<>();
  4. public Map<String, MyPojo> getMap() {
  5. return this.map;
  6. }
  7. }

考虑以下配置:
Properties:

  1. acme.map.key1.name=my name 1
  2. acme.map.key1.description=my description 1
  3. #---
  4. spring.config.activate.on-profile=dev
  5. acme.map.key1.name=dev name 1
  6. acme.map.key2.name=dev name 2
  7. acme.map.key2.description=dev description 2

Yaml:

  1. acme:
  2. map:
  3. key1:
  4. name: "my name 1"
  5. description: "my description 1"
  6. ---
  7. spring:
  8. config:
  9. activate:
  10. on-profile: "dev"
  11. acme:
  12. map:
  13. key1:
  14. name: "dev name 1"
  15. key2:
  16. name: "dev name 2"
  17. 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)的项。

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

2.7.8 属性转换

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

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

持续时间的转换

Spring Boot为表达持续时间提供了专门的支持。如果暴露一个java.time.Duration类型的属性,在应用程序属性中可以使用以下格式:

  • 用常规long类型数值的表现形式(使用毫秒作为默认单位,除非指定了@DurationUnit注解)
  • java.time.Duration使用的标准ISO-8601格式
  • 将数值和单位耦合的更易读格式(例如,10s表示10秒)

考虑以下示例:

  1. @ConfigurationProperties("app.system")
  2. public class AppSystemProperties {
  3. @DurationUnit(ChronoUnit.SECONDS)
  4. private Duration sessionTimeout = Duration.ofSeconds(30);
  5. private Duration readTimeout = Duration.ofMillis(1000);
  6. public Duration getSessionTimeout() {
  7. return this.sessionTimeout;
  8. }
  9. public void setSessionTimeout(Duration sessionTimeout) {
  10. this.sessionTimeout = sessionTimeout;
  11. }
  12. public Duration getReadTimeout() {
  13. return this.readTimeout;
  14. }
  15. public void setReadTimeout(Duration readTimeout) {
  16. this.readTimeout = readTimeout;
  17. }
  18. }

要指定一个会话的超时为30秒,使用30PT30S30s都是等效的。一个500毫秒的读取超时,可以的格式包括:500PT0.5S500ms
您还可以使用任何受支持的单位,包括:

  • ns ,十亿分之一秒
  • us ,微秒
  • ms ,毫秒
  • s ,秒
  • m ,分钟
  • h ,小时
  • d ,天

默认单位是毫秒,可以使用@DurationUnit覆盖默认值(使用上面的示例中所示的方法)。
如果您更喜欢使用构造函数绑定,则可以暴露这些相同的属性,如以下示例所示:

  1. @ConfigurationProperties("app.system")
  2. @ConstructorBinding
  3. public class AppSystemProperties {
  4. private final Duration sessionTimeout;
  5. private final Duration readTimeout;
  6. public AppSystemProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
  7. @DefaultValue("1000ms") Duration readTimeout) {
  8. this.sessionTimeout = sessionTimeout;
  9. this.readTimeout = readTimeout;
  10. }
  11. public Duration getSessionTimeout() {
  12. return this.sessionTimeout;
  13. }
  14. public Duration getReadTimeout() {
  15. return this.readTimeout;
  16. }
  17. }

灯泡.svg 如果要升级一个Long类型的时间属性值,如果定义的时间单位不是毫秒的话,请使用@DurationUnit确保时间的单位。这样做可以提供透明的升级途径,同时支持更丰富的格式。

日期的转换

Spring Boot除了对持续时间的支持外,还可以使用java.time.Period类型。在应用程序属性中,可以使用以下格式:

  • 常规int类型的表示形式(除非使用@PeriodUnit注解指定,否则使用天作为默认单位)
  • java.time.Period使用的标准ISO-8601格式
  • 将数值和单位耦合的更简洁格式(例如,1y3d表示1年零3天)

这种简洁格式支持以下单位:

  • y ,年
  • m ,月
  • w ,周
  • d ,天

info.svg java.time.Period类型从不实际存储星期数,这只是一个表示“ 7天”的快捷方式。

数据大小的转换

Spring框架使用DataSize类型的值表示字节数。应用程序属性中, 如果要暴露一个DataSize类型的属性,提供了以下格式:

  • 常规的long类型的数值的表示形式(除非已经使用@DataSizeUnit指定,否则使用字节作为默认单位)
  • 将值和单位耦合的更具可读性的格式(例如,10MB意味着10兆字节)

考虑以下示例:

  1. @ConfigurationProperties("app.io")
  2. public class AppIoProperties {
  3. @DataSizeUnit(DataUnit.MEGABYTES)
  4. private DataSize bufferSize = DataSize.ofMegabytes(2);
  5. private DataSize sizeThreshold = DataSize.ofBytes(512);
  6. public DataSize getBufferSize() {
  7. return this.bufferSize;
  8. }
  9. public void setBufferSize(DataSize bufferSize) {
  10. this.bufferSize = bufferSize;
  11. }
  12. public DataSize getSizeThreshold() {
  13. return this.sizeThreshold;
  14. }
  15. public void setSizeThreshold(DataSize sizeThreshold) {
  16. this.sizeThreshold = sizeThreshold;
  17. }
  18. }

指定一个10 兆字节的缓冲区大小,使用1000000010MB是等效的。指定一个256个字节的阈值,可以使用256256B
您也可以使用任何受支持的单位。它们是:

  • B ,字节
  • KB ,千字节
  • MB ,兆字节
  • GB ,千兆字节
  • TB ,太字节

默认单位是字节,可以使用@DataSizeUnit覆盖默认值(如上面的示例中所示的方法)。
如果您更喜欢使用构造函数绑定,则使用如下例所示的方法暴露这些相同的属性:

  1. @ConfigurationProperties("app.io")
  2. @ConstructorBinding
  3. public class AppIoProperties {
  4. private final DataSize bufferSize;
  5. private final DataSize sizeThreshold;
  6. public AppIoProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
  7. @DefaultValue("512B") DataSize sizeThreshold) {
  8. this.bufferSize = bufferSize;
  9. this.sizeThreshold = sizeThreshold;
  10. }
  11. public DataSize getBufferSize() {
  12. return this.bufferSize;
  13. }
  14. public DataSize getSizeThreshold() {
  15. return this.sizeThreshold;
  16. }
  17. }

灯泡.svg 如果要升级Long类型的属性,请使用@DataSizeUnit确保定义的单位(如果不是字节的话)。这样做可以提供透明的升级途径,同时支持更丰富的格式。

2.7.9 @ConfigurationProperties验证

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

  1. @ConfigurationProperties(prefix="acme")
  2. @Validated
  3. public class AcmeProperties {
  4. @NotNull
  5. private InetAddress remoteAddress;
  6. // ... getters and setters
  7. }

灯泡.svg 您还可以通过使用注解为@Bean方法(此方法使用@Validated创建配置属性)来触发验证。

为了确保始终为嵌套的属性触发验证,即使未找到任何属性,相关字段也必须使用@Valid注解。如下例(基于前面的AcmeProperties示例)所示:

  1. @ConfigurationProperties(prefix="acme")
  2. @Validated
  3. public class AcmeProperties {
  4. @NotNull
  5. private InetAddress remoteAddress;
  6. @Valid
  7. private final Security security = new Security();
  8. // ... getters and setters
  9. public static class Security {
  10. @NotEmpty
  11. public String username;
  12. // ... getters and setters
  13. }
  14. }

您还可以通过创建名为configurationPropertiesValidator的bean定义来添加自定义Spring Validator。该@Bean方法应声明成static类型。在应用程序生命周期中,配置属性验证器创建的是时期是非常早的,并且将@Bean方法声明为static类型可以在没有实例化@Configuration类的情况下使得Bean创建。这样做避免了由早期实例化引起的任何问题。

灯泡.svg spring-boot-actuator模块包括一个暴露所有@ConfigurationPropertiesbean的端点。将Web浏览器指向/actuator/configprops,或使用等效的JMX端点。 有关详细信息,请参见“生产就绪功能”部分。

2.7.10 @ConfigurationProperties与@Value

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

特征 @ConfigurationProperties @Value
宽松的绑定 受限(请参阅下面的注释)
元数据支持
SpEL 评价

info.svg 如果您确实想使用@Value,我们建议您以规范形式(仅使用小写字母的短横线命名法)引用属性名称。这将使Spring Boot可以使用与@ConfigurationProperties进行放松绑定的相同逻辑。例如,使用@Value("{demo.item-price}"),将从application.properties文件中采集demo.item-price和``demo.itemPrice格式的声明,也同时会从系统变量中查找DEMO_ITEMPRICE的定义。如果你使用的@Value("{demo.itemPrice}")demo.item-price和``DEMO_ITEMPRICE将不会予以考虑。

如果您为自己的组件定义了一组配置键,我们建议您将它们组合在以@ConfigurationProperties标记的POJO中。这样做将为您提供结构化、类型安全的对象,您可以将其注入到自己的bean中。

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