上一篇[24.7.4、YAML 的缺点]
下一篇[@ConfigurationProperties 验证]
英文原文:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-external-config.html#boot-features-external-configGitHub:https://github.com/jijicai/Spring/tree/master/spring-boot
24.8、类型安全的配置属性
使用 @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 roles = new ArrayList<>(Collections.singleton("USER")); public String getUsername() { ... } public void setUsername(String username) { ... } public String getPassword() { ... } public void setPassword(String password) { ... } public List getRoles() { ... } public void setRoles(List roles) { ... } }}
上面的 POJO 定义了以下属性:
(1)acme.enabled,默认值为 false。
(2)acme.remote-address,其类型可以从 String 转换过来。
(3)acme.security.username,具有嵌套的“security”对象,其名称由属性的名称确定。特别是,返回类型根本没有使用,它可能是 SecurityProperties。
(4)acme.security.password。
(5)acme.security.roles,包含一个字符串集合。
注释:getter 和 setter 通常是必需的,因为绑定是通过标准的 Java Beans 属性描述符进行的,就像 Spring MVC 一样。在下列情况下,可省略 setter:
(1)Maps,只要它们被初始化,就需要一个 getter,但不一定需要 setter,因为绑定器可以对它们进行修改。
(2)可以通过索引(通常使用 YAML)或使用单个逗号分隔值(属性)访问集合和数组。在后一种情况下,setter 是必需的。我们建议始终为这样的类型添加 setter。如果初始化集合,请确保它不是不可变的(如前一个示例所示)。
(3)如果嵌套的 POJO 属性被初始化(就像前面例子中 Security 字段),则 setter 不是必须的。如果希望绑定器使用实例的默认构造函数动态创建它,则需要一个 setter。
有些人使用 Project Lombok 自动添加 getter 和 setter。确保 Lombok 不会为这样的类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。
最后,只考虑标准的 Java Bean 属性,不支持绑定静态属性。
提示:另请参见 @Value 和 @ConfigurationProperties 之间的区别。(https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-external-config.html#boot-features-external-config-vs-value )
你还需要在 @EnableConfigurationProperties 注解中列出要注册的属性类,如下面的示例所示:
@Configuration@EnableConfigurationProperties(AcmeProperties.class)public class MyConfiguration {}
注释:当 @ConfigurationProperties bean 以这种方式注册时,bean 有一个常规名称:-,其中 是在 @ConfigurationProperties 注解中指定的环境键前缀, 是 bean 的完全限定名。如果该注解没有提供任何前缀,则只使用 bean 的全完限定名。
上面例子中的 bean 名称是 acme-com.example.AcmeProperties。
前面的配置为 AcmeProperties 创建一个常规 bean。我们建议 @ConfigurationProperties 只处理环境,特别是不要从上下文中注入其他 bean。请记住,@EnableConfigurationProperties 注解也会自动应用到你的项目中,以便从 Environment 配置任何带有 @ConfigurationProperties 注解的现有 bean。不是用 @EnableConfigurationProperties(AcmeProperties.class) 注解 MyConfiguration,你可以使 AcmeProperties 成为一个 bean,如下面的示例所示:
@Component@ConfigurationProperties(prefix="acme")public class AcmeProperties { // ... see the preceding example}
这种配置方式与 SpringApplication 外部的 YAML 配置配合得特别好,如下面示例所示:
# application.ymlacme: remote-address: 192.168.1.1 security: username: admin roles: - USER - ADMIN# additional configuration as required
要使用 @ConfigurationProperties bean,可以用与任何其他 bean 相同的方式注入它们,如下所示:
@Servicepublic 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 可以使用这些文件为自己的 keys 提供自动完成功能。详见附录B:配置元数据附录(https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/configuration-metadata.html )。
24.8.1、第三方配置
除了使用 @ConfigurationProperties 注解类之外,还可以在公共 @Bean 方法上使用它。如果要将属性绑定到不在你控制范围内的第三方组件,那么这样做特别有用。
要从 Environment 属性配置 bean,请将 @ConfigurationProperties 添加到其 bean 注册中,如下所示:
@ConfigurationProperties(prefix = "another")@Beanpublic AnotherComponent anotherComponent() { ...}
用 another 前缀定义的任何属性都映射到 AnotherComponent bean,其方式与前面的 AcmeProperties 示例类似。
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:宽松绑定
属性注释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:每个属性源的宽松绑定规则
属性源简单的(Simple)列表(List)属性文件驼峰式、烤串式或下划线式使用“[]”的标准列表语法或逗号分隔值。YAML 文件驼峰式、烤串式或下划线式标准 YAML 列表语法或逗号分隔值。环境变量以下划线为分隔符的大写格式。“_”不应在属性名中使用。下划线环绕的数字值,例如:MY_ACME_1_OTHER = my.acme[1].other系统属性驼峰式、烤串式或下划线式使用“[]”的标准列表语法或逗号分隔值。
提示:我们建议尽可能将属性存储为小写的烤串格式,例如:my.property-name=acme。
在绑定到 Map 属性时,如果 key 包含除小写字母-数字字符或“-”之外的任何内容,则需要使用方括号,以便保留原始值。如果没有用 [] 包围 key,则会删除不是字母数字或“-”的任何字符。例如,考虑将以下属性绑定到 Map:
acme: map: "[/key1]": value1 "[/key2]": value2 /key3: value3
上面的属性将绑定到以 /key1、/key2 和 key3 作为键的 Map。
上一篇[24.7.4、YAML 的缺点]
下一篇[@ConfigurationProperties 验证]