2. Externalized Configuration

SpringBoot 容许你将配置外部化(从jar包外引用配置),所以,你可以将同一份代码运行到不同的环境中。你可以使用 properties 文件、YAML文件,environment 变量 和 命令行的方式进行外部化配置。配置文件中的值可以通过 @Value 注解直接注入到 Bean 中,也可以使用 Spring Environment 进行访问,或者通过使用 @ConfigurationProperties 注解绑定到结构化对象。

Spring Boot使用一种非常特殊的PropertySource顺序,其设计目的是允许合理地覆盖值。

  1. Devtools global settings properties in the $HOME/.config/spring-boot when devtools is active。
  2. @TestPropertySource annotations on your tests.
  3. @TestPropertySource annotations on your tests
  4. Command line arguments;
  5. Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
  6. ServletConfig init parameters.
  7. ServletContext init parameters.
  8. JNDI attributes from java:comp/env.
  9. Java System properties (System.getProperties()).
  10. OS environment variables
  11. A RandomValuePropertySource that has properties only in random.*.
  12. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants).
  13. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
  14. Application properties outside of your packaged jar (application.properties and YAML variants).
  15. Application properties packaged inside your jar (application.properties and YAML variants).
  16. @PropertySource annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
  17. Default properties (specified by setting SpringApplication.setDefaultProperties).

下面提供一个具体的示例,假设你开发一个 @Component 组件使用 name 属性,如下代码片段所示:

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

@Component
public class MyBean {

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

    // ...

}

在你的类路径中(jar 包内),你可以有一个 application.properties 文件里面为 name 属性提供一个合理的默认值。当运行在一个新的环境中时,application.properties 文件可以被外置的配置文件替换。你启动的时候可以通过命令行替换name的值(java -jar app.jar --name="Spring")。

SPRING_APPLICATION_JSON 属性可以通过命令行设置环境变量,示例,

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

在前面的示例中,您将在Spring环境中使用acme.name=test。您还可以将JSON作为spring.application提供。系统属性中的json,如下面的示例所示

$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar

You can also supply the JSON by using a command line argument, as shown in the following example:

$ java -jar myapp.jar --spring.application.json='{"name":"test"}'

You can also supply the JSON as a JNDI variable, as follows: java:comp/env/spring.application.json.

2.1 Configuring Random Values

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

The random.int* syntax is OPEN value (,max) CLOSE where the OPEN,CLOSE are any character and value,max are integers. If max is provided, then value is the minimum value and max is the maximum value (exclusive)

random.int *语法是OPEN值(,max)CLOSE,其中OPEN,CLOSE是任何字符,value,max是整数。 如果提供了max,则value是最小值,而max是最大值(不包括)。

2.2 Accessing Command Line Properties

默认情况下,SpringApplication 会将所有的命令行参数(以--为前缀,比如 --server.port=9000)转换为属性并添加到 Environment中。如前面所述,命令行属性的优先级高于其他数据源的优先级

如果你不想要命令行属性添加到 Environment 环境变量,你可以通过 SpringApplication.setAddCommandLineProperties(false) 方法关闭此功能。

2.3 Application Property Files

SpringApplication 从如下地址的 application.properties 文件加载属性并添加到 Environment 环境中。

  1. 当前目录下的 config 目录
  2. 当前目录
  3. classpath 下的 /config 目录
  4. classpath 根目录

上述配置文件地址列表加载顺序是从上到下加载。

你可以使用 yaml 文件替换 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

spring.config.name 和 spring.config.location 配置文件告诉系统需要加载哪个配置文件,他们必须指定一个 environment properties(OS environment variable,a system property,command-line)

如果 spring.config.location 中包含目录(非具体文件),需要以 / 结尾(并且在运行时会自动追加 spring.config.name 的名称).spring.config.location 指定的文件会按原样使用,不支持任何形式的重写和覆盖。

配置文件路径按照相反的顺序进行搜索。默认情况下,配置文件地址的顺序如下所示:

1. classpath:/, 
2. classpath:/config/, 
3. file:./, 
4. 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 instead of spring.config.name

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

2.4 Profile-specific Properties

除了使用 application.properties 文件外,profile-specific properties 也可以按照如下命名规范来定义属性文件:

application-{profile}.properties

在 Environment 环境中,如果没有设置策略文件的情况下会默认提供一个default文件,换言之,系统会从 application-default.properties 文件加载属性。

Profile-specific 属性文件会从 application.properties 所在目录进行加载,profile-specific 文件总是会覆盖非 profile-specific ,不管 profile-specific 文件在jar包内或jar包外。

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

如果使用 spring.config.location 指定的文件路径,那任何路径外的配置文件都不会被考虑使用,不过可以添加到 spring.config.location 路径中使配置生效。

2.5 Placeholders in Properties

application.properties 中的属性在被使用的时候会被当前 Environment 过滤,所有你可以引用一些之前定义的值(for example,form System properties).

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

You can also use this technique to create “short” variants of existing Spring Boot properties. See the Howto.html how-to for details.

2.6 Encryping Properties

SpringBoot 不支持任何内置的属性加密技术,但是,它提供了修改 Spring Environment 中属性值得挂载点。EnvironmentPostProcessor 接口容许你在程序启动之前操作 Environment 属性。看考详细信息hwoto.html

如果你想要找一种安全的方式来存储证书和密码,你可以使用 Spring Cloud Vault 项目来存储外部配置到 HashiCorp Vault

2.7 Using YAML Instead of Properties

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

If you use “Starters”, SnakeYAML is automatically provided by spring-boot-starter.

2.7.1 Loading YAML

Spring Framework 提供两个方便的类来加载 YAML 文件。YamlPropertiesFactoryBean 将 YAML 作为属性文件进行加载,YamlMapFactoryBean 将 YAML 作为 Map 加载。

YamlPropertiesFactoryBean

environments:
    dev:
        url: https://dev.example.com
        name: Developer Setup
    prod:
        url: https://another.example.com
        name: My Cool App
        
上面内容将转换为如下属性
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App

YamlMapFactoryBean

YAML 清单的属性元素可以使用 [index] 替换,如下所示:

my:
   servers:
       - dev.example.com
       - another.example.com
       
my.servers[0]=dev.example.com
my.servers[1]=another.example.com

可以使用 Spring Boot‘s Binder 工具来绑定属性(参考 @ConfigurationProperties),在目标对象中你需要有一个 List 或者 Set 对象并且需要提供 setter 或者初始化方法。参考如下示例,绑定了上面定义的属性到对象:

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

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

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

2.7.2 Exposing YAML as Properties in the spring Envronment

YamlPropertySourceLoader 类可以在 Spring Environment 环境中以 PropertySource 的形式暴露 YAML 属性,所以你可以使用 @Value 注解及占位符来访问 YAML 属性。

2.7.3 Multi-profile YAML Documents

在 单个 YAML 文档中你可以使用 profile-specific 关键字对多个文档内容进行区分,如下所示:

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 and eu-central 配置激活,server.address 属性是 192.168.1.120,如果 development、production、eu-central 都没有激活,server.address 地址是 192.168.1.100

如果在启动应用程序上下文时未显式激活任何配置,则会激活默认配置文件。 因此,在以下YAML中,我们为spring.security.user.password设置了一个值,该值仅在“默认”配置文件中可用

server:
  port: 8000
  
---
spring:
  profiles: default
  security:
    user:
      password: weak

而在下面配置中,密码将会一直被使用因为它部署任何配置,并且你可以在需要的时候显示的进行重写。

server:
  port: 8000
spring:
  security:
    user:
      password: weak

Spring profiles designated by using the spring.profiles element may optionally be negated by using the ! character. If both negated and non-negated profiles are specified for a single document, at least one non-negated profile must match, and no negated profiles may match.

2.7.4 YAML Shortcomings

YAML 文件不能使用 @PropertySource 注解进行加载。所有,在这种情况下你需要使用 properties 属性文件。
Using the multi YAML document syntax in profile-specific YAML files can lead to unexpected behavior. For example, consider the following config in a file:

application-dev.yml
server:
  port: 8000
  
---
spring:
  profiles: "!test"
  security:
    user:
      password: "secret"

如果使用参数--spring.profiles.active = dev运行应用程序,则可能希望将security.user.password设置为“ secret”,但事实并非如此。

当前文档的嵌套结构将会被忽略因为当前文件的名称是 application-dev.yml。它已经是一个 profile-specific 文件并且结构将会被忽略。

We recommend that you don’t mix profile-specific YAML files and multiple YAML documents. Stick to using only one of them.
不建议混合的 profile-specific yaml 文件和 多个 yaml 文档一起使用,选择其一即可。

2.8 Type-sage Configuration Properties

在某些情况下使用 @Value("${}") 注解进行配置属性的注入有些麻烦,尤其是在多配置文件或者你的数据结构有等级限制。Spring Boot提供了另一种处理属性的方法,允许强类型bean控制和验证应用程序的配置。

关于 @Value 和 Type-sage Configuration Properties 的区别请参考 2.8.10 章节

2.8.1 JavaBean properties binding

绑定属性到一个标准的 JavaBean 属性,如下所示:

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) { ... }

    }
}

上述代码片段中的对象定义了下面几个属性:

  • acme.enabled
  • acme.remote-address
  • acme.security.username
  • acme.security.password
  • acme.security.roles

SpringBoot 中绑定属性到 @ConfigurationProperties 注解的类中的变量,可以使用 properties files、YAML files、environment vriables 等,但是该类的(settter/getter)方法不能直接访问。

这种安排依赖于默认的空构造函数,getter和setter通常是必需的,因为绑定是通用的标准的Java bean属性描述符进行的,就像Spring MVC一样。在下列情况下,可以省略setter方法;

  • Maps, as long as they are initialized, need a getter but not necessarily a setter, since they can be mutated by the binder.
  • Collections and arrays can be accessed either through an index (typically with YAML) or by using a single comma-separated value (properties). In the latter case, a setter is mandatory. We recommend to always add a setter for such types. If you initialize a collection, make sure it is not immutable (as in the preceding example).
  • If nested POJO properties are initialized (like the Security field in the preceding example), a setter is not required. If you want the binder to create the instance on the fly by using its default constructor, you need a setter.

Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.

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

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

2.8.2 Constructor binding

上一节中的示例可以用不可变的方式重写,如下面的示例所示:

package com.example;

import java.net.InetAddress;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.DefaultValue;

@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties {

    private final boolean enabled;
    private final InetAddress remoteAddress;
    private final Security security;
    public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }

    public boolean isEnabled() { ... }
    public InetAddress getRemoteAddress() { ... }
    public Security getSecurity() { ... }
    public static class Security {

        private final String username;
        private final String password;
        private final List<String> roles;

        public Security(String username, String password,
                @DefaultValue("USER") List<String> roles) {
            this.username = username;
            this.password = password;
            this.roles = roles;
        }

        public String getUsername() { ... }
        public String getPassword() { ... }
        public List<String> getRoles() { ... }
    }
}

在此设置中,@ConstructorBinding批注用于指示应使用构造函数绑定。 这意味着绑定器将期望找到带有您希望绑定的参数的构造函数。

@ConstructorBinding类的嵌套成员(如上面示例中的安全性)也将通过其构造函数绑定。

Default values can be specified using @DefaultValue and the same conversion service will be applied to coerce the String value to the target type of a missing property.

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

要使用构造函数绑定,必须使用@EnableConfigurationProperties或配置属性扫描来启用该类。如果当前Bean 是根据 spring 机制创建的则不能使用构造函数绑定(e.g. @Component beans, beans created via @Bean methods or beans loaded using @Import)

2.8.3 Enabling @ConfigurationProperties-annotatedtypes

SpringBoot 提供了基础设施来绑定 @ConfigurationProperties 类型和注册成为bean。你可以按照类逐个扫描也可以按照组件扫描的方式进行启用。

有些时候,添加 @ConfigurationProperties 注解的类可能不适用以扫描的方式注册为bean。比如,你需要自定义注解或者按条件的启用。在这种情况下,使用 @EnableConfigurationProperties 注解指定需要处理的列表。这个可以应用在任何标有 @Configuration 注解的类上,示例如下:

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

要使用配置属性扫描,添加 @ConfigurationPropertiesSan 注解到你的主程序。通常情况下,你可以添加这个注解到带有 @SpringBootApplication 的主类中或者添加到带有 @Configuration 的类中。默认情况下,将会对添加注解的包进行扫描,如果你想要自定义扫描路径,你可参考如下示例配置:

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

使用配置属性扫描或通过@EnableConfigurationProperties注册@ConfigurationProperties Bean时,该Bean具有常规名称:<prefix>-<fqn>,其中<prefix>是@ConfigurationProperties指定的前缀和<fqn>是Bean的完全限定名称。如果 ConfigurationProperties 不提供任何前缀,则仅使用Bean的完全限定名称。

The bean name in the example above is acme-com.example.AcmeProperties

We recommend that @ConfigurationProperties only deal with the environment and, in particular, does not inject other beans from the context. For corner cases, setter injection can be used or any of the *Aware interfaces provided by the framework (such as EnvironmentAware if you need access to the Environment). If you still want to inject other beans using the constructor, the configuration properties bean must be annotated with @Component and use JavaBean-based property binding.

2.8.4 Using @ConfigurationProperties-annotated types

这种配置样式与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());
        // ...
    }

}

Using @ConfigurationProperties also lets you generate metadata files that can be used by IDEs to offer auto-completion for your own keys. See the appendix for details.

2.8.5 Thrid-party Configuration

除了使用@ConfigurationProperties来注释一个类外,您还可以在公共@Bean方法上使用它。当您希望将属性绑定到您无法控制的第三方组件时,这样做特别有用。

要从环境属性配置bean,请将@ConfigurationProperties添加到它的bean注册中,如下面的示例所示

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

2.8.6 Relaxed Binding

SpringBoot 为 属性变量绑定到具体的bean提供了比较宽松的规则,所以,属性文件名称和 Environment 的属性名称不需要完全一致,比如,context-path 可以绑定到 contextPath。参考如下示例:

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

}

With the preceding code, the following properties names can all be used:

Property Note
acme.my-project.person.first-name 推荐在.properties和.yml文件中使用
acme.myProject.person.firstName 标准的驼峰匹配语法
acme.my_project.person.first_name 下划线表示法,它是.properties和.yml文件中使用的另一种格式
ACME_MYPROJECT_PERSON_FIRSTNAME 大写格式,在使用系统环境变量时推荐使用这种格式

The prefix value for the annotation must be in kebab case (lowercase and separated by -, such as acme.my-project.person).

Property Source Simple List
Properties Files Camel case, kebab case, or underscore notation 使用[]或逗号分隔值的标准列表语法
YAML Files Camel case, kebab case, or underscore notation 标准的YAML列表语法或逗号分隔的值
Environment Variables 用下划线作为分隔符的大写格式。不应该在属性名中使用 Numeric values surrounded by underscores, such as MY ACME 1 OTHER = my.acme[1].other
System properties Camel case, kebab case, or underscore notation 使用[]或逗号分隔值的标准列表语法

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

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

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

The properties above will bind to a Map with /key1, /key2 and key3 as the keys in the map.

对于YAML文件,方括号需要用引号括起来,以便正确地解析

2.8.7 Merging Complex Types

如果在多个位置配置了列表,则通过替换整个列表来进行覆盖,在下面的示例中,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 选项激活,list 属性任然包含一个属性(name 的值是 my another name ,description 的值是 null)。这个配置不会添加第二个 MyPojo 实例到 list,并且不会进行覆盖。

当进行了多个 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 选项激活,AcmeProperties.list 中将包含一个 MyPojo 实体(name = my another name ,description = null). 对于YAML,可以使用逗号分隔的列表和YAML列表来完全覆盖列表的内容。

对于 Map 属性,你可以从多个数据源进行属性的绑定。然而,对于多个数据源的一个属性,会使用优先级最高的属性值。下面示例将暴露一个 Map<String, MyPojo> 从 AcmeProperties 对象。

@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)

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

2.8.8 Properties Conversion

当Spring Boot绑定到@ConfigurationProperties bean时,它尝试将外部应用程序属性强制转换为正确的类型.如果你想自定义转换规则,你可以提供一个 ConversionService bean 或者 自定义 editors 属性 或者 自定义 Converters。

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

2.8.8.1 Converting durations

SpringBoot 在时间转换上有专用的支持,如果你暴露一个 java.time.Duration 属性,下面格式在 application.properties 中可以生效。

  • A regular long representation (using milliseconds as the default unit unless a @DurationUnit has been specified)
  • The standard ISO-8601 format used by java.time.Duration
  • A more readable format where the value and the unit are coupled (e.g. 10s means 10 seconds)

参考下面代码片段

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

}

To specify a session timeout of 30 seconds, 30, PT30S and 30s are all equivalent. A read timeout of 500ms can be specified in any of the following form: 500, PT0.5S and 500ms.

You can also use any of the supported units. These are:

  • ns for nanoseconds
  • us for microseconds
  • ms for milliseconds
  • s for seconds
  • m for minutes
  • h for hours
  • d for days

The default unit is milliseconds and can be overridden using @DurationUnit as illustrated in the sample above

2.8.9 @ConfigurationProperties Validation

SpringBoot 尝试着验证 @ConfigurationProperties 注解配置的类当此类同时也配置了 @Validated 注解。You can use JSR-303 javax.validation constraint annotations directly on your configuration class. 为此,请确保classpath上有一个兼容的JSR-303实现,然后将约束注释添加到字段中,如下面的示例所示

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

    @NotNull
    private InetAddress remoteAddress;

    // ... getters and setters

}

You can also trigger validation by annotating the @Bean method that creates the configuration properties with @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 方法应该定义为静态的。配置属性验证器在程序生命周期内早期就被创建,并且 @Bean 方法已静态的方式被创建且不需要实例化 @Configuration 类。这样做可以避免任何可能由早期实例化引起的问题。

The spring-boot-actuator module includes an endpoint that exposes all @ConfigurationProperties beans. Point your web browser to /actuator/configprops or use the equivalent JMX endpoint. See the "Production ready features" section for details.

2.8.10 @ConfigurationProperties vs. @Value

@Value注解是容器的核心功能,它不提供与类型安全(type-safe)配置属性相同的特性.The following table summarizes the features that are supported by @ConfigurationProperties and @Value:

Feature @ConfigurationProperties @Valu
Relaxed binding yes no
Meta-data support yes no
SpEL evaluation no yes

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容