Guide to @ConfigurationProperties in Spring Boot – Spring Boot中的@ConfigurationProperties指南

最后修改: 2017年 4月 24日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

Spring Boot has many useful features including externalized configuration and easy access to properties defined in properties files. An earlier tutorial described various ways in which this could be done.

Spring Boot 有许多有用的功能,包括外部化配置和轻松访问属性文件中定义的属性。早期的教程描述了可以实现这一目标的各种方法。

We are now going to explore the @ConfigurationProperties annotation in greater detail.

我们现在要更详细地探讨@ConfigurationProperties注解。

2. Setup

2.设置

This tutorial uses a fairly standard setup. We start by adding spring-boot-starter-parent as the parent in our pom.xml:

本教程使用一个相当标准的设置。我们首先添加spring-boot-starter-parent作为我们pom.xml中的父节点。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.4</version>
    <relativePath/>
</parent>

To be able to validate properties defined in the file, we also need an implementation of JSR-303, and hibernate-validator is one of them.

为了能够验证文件中定义的属性,我们还需要一个JSR-303的实现,而hibernate-validator就是其中之一。

Let’s add it to our pom.xml as well:

让我们把它也添加到我们的pom.xml

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>6.0.16.Final</version>
</dependency>

The “Getting Started with Hibernate Validator” page has more details.

“Hibernate验证器入门”页面有更多细节。

3. Simple Properties

3.简单属性

The official documentation advises that we isolate configuration properties into separate POJOs.

官方文档建议我们将配置属性隔离到单独的POJO中

So let’s start by doing that:

因此,让我们从这一点做起。

@Configuration
@ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
    
    private String hostName;
    private int port;
    private String from;

    // standard getters and setters
}

We use @Configuration so that Spring creates a Spring bean in the application context.

我们使用@Configuration,以便Spring在应用上下文中创建一个Spring Bean。

@ConfigurationProperties works best with hierarchical properties that all have the same prefix; therefore, we add a prefix of mail.

@ConfigurationProperties对所有具有相同前缀的分层属性效果最好;因此,我们添加一个mail的前缀。

The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.

Spring框架使用标准的Java Bean设置器,所以我们必须为每个属性声明设置器。

Note: If we don’t use @Configuration in the POJO, then we need to add @EnableConfigurationProperties(ConfigProperties.class) in the main Spring application class to bind the properties into the POJO:

注意:如果我们不在POJO中使用@Configuration,那么我们需要在Spring主应用类中添加@EnableConfigurationProperties(ConfigProperties.class)来将属性绑定到POJO中。

@SpringBootApplication
@EnableConfigurationProperties(ConfigProperties.class)
public class EnableConfigurationDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(EnableConfigurationDemoApplication.class, args);
    }
}

That’s it! Spring will automatically bind any property defined in our property file that has the prefix mail and the same name as one of the fields in the ConfigProperties class.

这就是了!Spring将自动绑定任何在我们的属性文件中定义的、具有mail前缀且与ConfigProperties类中的一个字段名称相同的属性

Spring uses some relaxed rules for binding properties. As a result, the following variations are all bound to the property hostName:

Spring对绑定属性使用了一些宽松的规则。因此,以下变化都被绑定到属性hostName

mail.hostName
mail.hostname
mail.host_name
mail.host-name
mail.HOST_NAME

Therefore, we can use the following properties file to set all the fields:

因此,我们可以使用下面的属性文件来设置所有字段。

#Simple properties
mail.hostname=host@mail.com
mail.port=9000
mail.from=mailer@mail.com

3.1. Spring Boot 2.2

3.1.Spring Boot 2.2

As of Spring Boot 2.2, Spring finds and registers @ConfigurationProperties classes via classpath scanning. Scanning of @ConfigurationProperties needs to be explicitly opted into by adding the @ConfigurationPropertiesScan annotation. Therefore, we don’t have to annotate such classes with @Component (and other meta-annotations like @Configuration), or even use the @EnableConfigurationProperties:

Spring Boot 2.2开始,Spring通过classpath扫描找到并注册@ConfigurationProperties。扫描@ConfigurationProperties需要通过添加@ConfigurationPropertiesScan注解来明确选择。因此,我们不必用@Component (和其他元注解,如@Configuration)来注解这些类, 甚至不必使用@EnableConfigurationProperties:

@ConfigurationProperties(prefix = "mail") 
@ConfigurationPropertiesScan 
public class ConfigProperties { 

    private String hostName; 
    private int port; 
    private String from; 

    // standard getters and setters 
}

The classpath scanner enabled by @SpringBootApplication finds the ConfigProperties class, even though we didn’t annotate this class with @Component.

@SpringBootApplication启用的classpath扫描器找到了ConfigProperties类,尽管我们没有用@Component.注释这个类。

In addition, we can use the @ConfigurationPropertiesScan annotation to scan custom locations for configuration property classes:

此外,我们可以使用@ConfigurationPropertiesScan注解来扫描自定义位置的配置属性类:

@SpringBootApplication
@ConfigurationPropertiesScan("com.baeldung.configurationproperties")
public class EnableConfigurationDemoApplication { 

    public static void main(String[] args) {   
        SpringApplication.run(EnableConfigurationDemoApplication.class, args); 
    } 
}

This way Spring will look for configuration property classes only in the com.baeldung.properties package.

这样,Spring将只在com.baeldung.properties包中寻找配置属性类。

4. Nested Properties

4.嵌套属性

We can have nested properties in Lists, Maps, and Classes.

我们可以在列表、地图类中拥有嵌套属性。

Let’S create a new Credentials class to use for some nested properties:

让我们创建一个新的Credentials类,用于一些嵌套属性。

public class Credentials {
    private String authMethod;
    private String username;
    private String password;

    // standard getters and setters
}

We also need to update the ConfigProperties class to use a List, a Map, and the Credentials class:

我们还需要更新ConfigProperties类,以使用List、Map,以及Credentials类。

public class ConfigProperties {

    private String host;
    private int port;
    private String from;
    private List<String> defaultRecipients;
    private Map<String, String> additionalHeaders;
    private Credentials credentials;
 
    // standard getters and setters
}

The following properties file will set all the fields:

下面的属性文件将设置所有字段。

#Simple properties
mail.hostname=mailer@mail.com
mail.port=9000
mail.from=mailer@mail.com

#List properties
mail.defaultRecipients[0]=admin@mail.com
mail.defaultRecipients[1]=owner@mail.com

#Map Properties
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true

#Object properties
mail.credentials.username=john
mail.credentials.password=password
mail.credentials.authMethod=SHA1

5. Using @ConfigurationProperties on a @Bean Method

5.在@Bean方法上使用@ConfigurationProperties

We can also use the @ConfigurationProperties annotation on @Bean-annotated methods.

我们也可以在@Bean注释的方法上使用@ConfigurationProperties注释。

This approach may be particularly useful when we want to bind properties to a third-party component that’s outside of our control.

当我们想把属性绑定到一个不受我们控制的第三方组件上时,这种方法可能特别有用。

Let’s create a simple Item class that we’ll use in the next example:

让我们创建一个简单的Item类,我们将在下一个例子中使用。

public class Item {
    private String name;
    private int size;

    // standard getters and setters
}

Now let’s see how we can use @ConfigurationProperties on a @Bean method to bind externalized properties to the Item instance:

现在让我们看看如何在@Bean方法上使用@ConfigurationProperties来将外部化的属性绑定到Item实例。

@Configuration
public class ConfigProperties {

    @Bean
    @ConfigurationProperties(prefix = "item")
    public Item item() {
        return new Item();
    }
}

Consequently, any item-prefixed property will be mapped to the Item instance managed by the Spring context.

因此,任何项目限定的属性将被映射到Spring上下文管理的Item实例。

6. Property Validation

6.属性验证

@ConfigurationProperties provides validation of properties using the JSR-303 format. This allows all sorts of neat things.

@ConfigurationProperties提供了使用JSR-303格式的属性验证。这允许各种整洁的东西。

For example, let’s make the hostName property mandatory:

例如,让我们把hostName属性变成强制性的。

@NotBlank
private String hostName;

Next, let’s make the authMethod property from 1 to 4 characters long:

接下来,让我们把authMethod属性的长度从1到4个字符。

@Length(max = 4, min = 1)
private String authMethod;

Then the port property from 1025 to 65536:

然后将port属性从1025到65536。

@Min(1025)
@Max(65536)
private int port;

Finally, the from property must match an email address format:

最后,from属性必须符合电子邮件地址格式。

@Pattern(regexp = "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$")
private String from;

This helps us reduce a lot of if – else conditions in our code, and makes it look much cleaner and more concise.

这有助于我们减少代码中大量的if – else条件,并使代码看起来更干净、更简明。

If any of these validations fail, then the main application would fail to start with an IllegalStateException.

如果这些验证中的任何一个失败了,那么主应用程序就会以IllegalStateException的形式启动失败。

The Hibernate Validation framework uses standard Java bean getters and setters, so it’s important that we declare getters and setters for each of the properties.

Hibernate验证框架使用标准的Java Bean getters和setters,所以我们为每个属性声明getters和setters是很重要的。

7. Property Conversion

7.财产转换

@ConfigurationProperties supports conversion for multiple types of binding the properties to their corresponding beans.

@ConfigurationProperties支持转换多种类型的绑定属性到其相应的Bean。

7.1. Duration

7.1.期限

We’ll start by looking at converting properties into Duration objects.

我们将首先研究如何将属性转换为Duration 对象

Here we have two fields of type Duration:

这里我们有两个类型为Duration的字段。

@ConfigurationProperties(prefix = "conversion")
public class PropertyConversion {

    private Duration timeInDefaultUnit;
    private Duration timeInNano;
    ...
}

This is our properties file:

这就是我们的属性文件。

conversion.timeInDefaultUnit=10
conversion.timeInNano=9ns

As a result, the field timeInDefaultUnit will have a value of 10 milliseconds, and timeInNano will have a value of 9 nanoseconds.

因此,字段timeInDefaultUnit的值为10毫秒,而timeInNano的值为9纳秒。

The supported units are ns, us, ms, s, m, h and d for nanoseconds, microseconds, milliseconds, seconds, minutes, hours, and days, respectively.

支持的单位是ns、us、ms、s、m、hd,分别代表纳秒、微秒、毫秒、秒、分钟、小时和天。

The default unit is milliseconds, which means if we don’t specify a unit next to the numeric value, Spring will convert the value to milliseconds.

默认单位是毫秒,这意味着如果我们不在数值旁边指定单位,Spring将把数值转换为毫秒。

We can also override the default unit using @DurationUnit:

我们还可以使用@DurationUnit:覆盖默认单位。

@DurationUnit(ChronoUnit.DAYS)
private Duration timeInDays;

This is the corresponding property:

这就是相应的属性。

conversion.timeInDays=2

7.2. DataSize

7.2.数据大小

Similarly, Spring Boot @ConfigurationProperties supports DataSize type conversion.

类似地,Spring Boot @ConfigurationProperties支持DataSize类型转换。

Let’s add three fields of type DataSize:

让我们添加三个类型为DataSize的字段。

private DataSize sizeInDefaultUnit;

private DataSize sizeInGB;

@DataSizeUnit(DataUnit.TERABYTES)
private DataSize sizeInTB;

These are the corresponding properties:

这些是相应的属性。

conversion.sizeInDefaultUnit=300
conversion.sizeInGB=2GB
conversion.sizeInTB=4

In this case, the sizeInDefaultUnit value will be 300 bytes, as the default unit is bytes.

在这种情况下,sizeInDefaultUnit值将是300字节,因为默认单位是字节。

The supported units are B, KB, MB, GB, and TB. We can also override the default unit using @DataSizeUnit.

支持的单位有B、KB、MB、GBTB。我们也可以使用@DataSizeUnit.覆盖默认单位。

7.3. Custom Converter

7.3.自定义转换器

We can also add our own custom Converter to support converting a property to a specific class type.

我们还可以添加我们自己的自定义Converter,以支持将一个属性转换为一个特定的类别类型。

Let’s add a simple class Employee:

让我们添加一个简单的类Employee

public class Employee {
    private String name;
    private double salary;
}

Then we’ll create a custom converter to convert this property:

然后我们将创建一个自定义转换器来转换这个属性。

conversion.employee=john,2000

We will convert it to a file of type Employee:

我们将把它转换为Employee类型的文件。

private Employee employee;

We will need to implement the Converter interface, then use @ConfigurationPropertiesBinding annotation to register our custom Converter:

我们将需要实现Converter接口,然后使用@ConfigurationPropertiesBinding注解来注册我们的自定义Converter

@Component
@ConfigurationPropertiesBinding
public class EmployeeConverter implements Converter<String, Employee> {

    @Override
    public Employee convert(String from) {
        String[] data = from.split(",");
        return new Employee(data[0], Double.parseDouble(data[1]));
    }
}

8. Immutable @ConfigurationProperties Binding

8.不可变的@ConfigurationProperties 绑定

As of Spring Boot 2.2, we can use the @ConstructorBinding annotation to bind our configuration properties.

从Spring Boot 2.2开始,我们可以使用@ConstructorBinding注解来绑定我们的配置属性

This essentially means that @ConfigurationProperties-annotated classes may now be immutable.

这基本上意味着@ConfigurationProperties-注释的类现在可以是immutable

@ConfigurationProperties(prefix = "mail.credentials")
@ConstructorBinding
public class ImmutableCredentials {

    private final String authMethod;
    private final String username;
    private final String password;

    public ImmutableCredentials(String authMethod, String username, String password) {
        this.authMethod = authMethod;
        this.username = username;
        this.password = password;
    }

    public String getAuthMethod() {
        return authMethod;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

As we can see, when using @ConstructorBinding, we need to provide the constructor with all the parameters we’d like to bind.

我们可以看到,当使用@ConstructorBinding时,我们需要向构造函数提供我们想绑定的所有参数。

Note that all the fields of ImmutableCredentials are final. Also, there are no setter methods.

注意,ImmutableCredentials的所有字段都是最终的。而且,没有setter方法。

Furthermore, it’s important to emphasize that to use the constructor binding, we need to explicitly enable our configuration class either with @EnableConfigurationProperties or with @ConfigurationPropertiesScan.

此外,需要强调的是,要使用构造器绑定。我们需要明确地启用我们的配置类,要么使用@EnableConfigurationProperties,要么使用@ConfigurationPropertiesScan

9. Java 16 records

9.Java 16条记录s

Java 16 introduced the record types as part of JEP 395. Records are classes that act as transparent carriers for immutable data. This makes them perfect candidates for configuration holders and DTOs. As a matter of fact, we can define Java records as configuration properties in Spring Boot. For instance, the previous example can be rewritten as:

Java 16引入了记录类型,作为JEP 395的一部分。记录是作为不可变数据的透明载体的类。这使它们成为配置持有者和DTO的完美候选者。事实上,我们可以将Java记录定义为Spring Boot的配置属性。例如,前面的例子可以改写为。

@ConstructorBinding
@ConfigurationProperties(prefix = "mail.credentials")
public record ImmutableCredentials(String authMethod, String username, String password) {
}

Obviously, it’s more concise compared to all those noisy getters and setters.

显然,与所有那些嘈杂的getters和setters相比,它更简洁。

Moreover, as of Spring Boot 2.6, for single-constructor records, we can drop the @ConstructorBinding annotation. If our record has multiple constructors, however, @ConstructorBinding should still be used to identify the constructor to use for property binding.

此外,从Spring Boot 2.6开始,对于单构造函数记录,我们可以放弃@ConstructorBinding注解。但是,如果我们的记录有多个构造函数,@ConstructorBinding仍应被用来确定用于属性绑定的构造函数。

10. Conclusion

10.结论

In this article, we explored the @ConfigurationProperties annotation and highlighted some of the useful features it provides, like relaxed binding and Bean Validation.

在这篇文章中,我们探讨了@ConfigurationProperties注解,并强调了它提供的一些有用的功能,如放松的绑定和Bean Validation。

As usual, the code is available over on Github.

像往常一样,代码可以在Github上获得。