Guide de @ConfigurationProperties dans Spring Boot

1. Introduction

Spring Boot possède de nombreuses fonctionnalités utiles, notamment une configuration externalisée et un accès facile aux propriétés définies dans les fichiers de propriétés . Un didacticiel précédent décrivait différentes manières de procéder.

Nous allons maintenant explorer l' annotation @ConfigurationProperties plus en détail.

2. Configuration

Ce tutoriel utilise une configuration assez standard. Nous commençons par ajouter spring-boot-starter-parent comme parent dans notre pom.xml :

 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE  

Pour pouvoir valider les propriétés définies dans le fichier, nous avons également besoin d'une implémentation de JSR-303, et hibernate-validator est l'un d'entre eux.

Ajoutons-le également à notre pom.xml :

 org.hibernate hibernate-validator 6.0.16.Final  

La page «Premiers pas avec Hibernate Validator» contient plus de détails.

3. Propriétés simples

La documentation officielle conseille d'isoler les propriétés de configuration dans des POJO séparés .

Alors commençons par faire ça:

@Configuration @ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; // standard getters and setters }

Nous utilisons @Configuration pour que Spring crée un bean Spring dans le contexte de l'application.

@ConfigurationProperties fonctionne mieux avec les propriétés hiérarchiques qui ont toutes le même préfixe; par conséquent, nous ajoutons un préfixe de mail .

Le framework Spring utilise des setters de bean Java standard, nous devons donc déclarer des setters pour chacune des propriétés.

Remarque: Si nous n'utilisons pas @Configuration dans le POJO, nous devons ajouter @EnableConfigurationProperties (ConfigProperties.class) dans la classe d'application Spring principale pour lier les propriétés dans le POJO:

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

C'est ça! Spring liera automatiquement toute propriété définie dans notre fichier de propriétés qui a le préfixe mail et le même nom que l'un des champs de la classe ConfigProperties .

Spring utilise des règles assouplies pour les propriétés de liaison. Par conséquent, les variantes suivantes sont toutes liées à la propriété hostName :

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

Par conséquent, nous pouvons utiliser le fichier de propriétés suivant pour définir tous les champs:

#Simple properties [email protected] mail.port=9000 [email protected] 

3.1. Botte de printemps 2.2

À partir de Spring Boot 2.2, Spring trouve et enregistre les classes @ConfigurationProperties via l'analyse du chemin de classe . Par conséquent, il n'est pas nécessaire d'annoter ces classes avec @Component (et d'autres méta-annotations telles que @Configuration), ni même d'utiliser @EnableConfigurationProperties:

@ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; // standard getters and setters } 

L'analyseur de chemin de classe activé par @SpringBootApplication trouve la classe ConfigProperties , même si nous n'avons pas annoté cette classe avec @Component.

De plus, nous pouvons utiliser l' annotation @ConfigurationPropertiesScan pour analyser les emplacements personnalisés pour les classes de propriétés de configuration:

@SpringBootApplication @ConfigurationPropertiesScan("com.baeldung.configurationproperties") public class EnableConfigurationDemoApplication { public static void main(String[] args) { SpringApplication.run(EnableConfigurationDemoApplication.class, args); } }

De cette façon, Spring recherchera les classes de propriétés de configuration uniquement dans le package com.baeldung.properties .

4. Propriétés imbriquées

Nous pouvons avoir des propriétés imbriquées dans des listes, des cartes et des classes.

Créons une nouvelle classe Credentials à utiliser pour certaines propriétés imbriquées:

public class Credentials { private String authMethod; private String username; private String password; // standard getters and setters }

Nous devons également mettre à jour la classe ConfigProperties pour utiliser une liste, une carte et la classe Credentials :

public class ConfigProperties { private String host; private int port; private String from; private List defaultRecipients; private Map additionalHeaders; private Credentials credentials; // standard getters and setters }

Le fichier de propriétés suivant définira tous les champs:

#Simple properties [email protected] mail.port=9000 [email protected] #List properties mail.defaultRecipients[0][email protected] mail.defaultRecipients[1][email protected] #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. Utilisation de @ConfigurationProperties sur une méthode @Bean

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

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:

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:

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

6. Property Validation

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

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

@NotBlank private String hostName;

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

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

Then the port property from 1025 to 65536:

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

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

@Pattern(regexp = "^[a-z0-9._%+-][email protected][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 any of these validations fail, then the main application would fail to start with an 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.

7. Property Conversion

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

7.1. Duration

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

Here we have two fields of type 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.

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

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.

We can also override the default unit using @DurationUnit:

@DurationUnit(ChronoUnit.DAYS) private Duration timeInDays;

This is the corresponding property:

conversion.timeInDays=2

7.2. DataSize

Similarly, Spring Boot @ConfigurationProperties supports DataSize type conversion.

Let's add three fields of type 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.

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

7.3. Custom Converter

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

Let's add a simple class 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:

private Employee employee;

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

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

8. Immutable @ConfigurationProperties Binding

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

This essentially means that @ConfigurationProperties-annotated classes may now be 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.

Notez que tous les champs d' ImmutableCredentials sont définitifs. De plus, il n'y a pas de méthodes de setter.

En outre, il est important de souligner que pour utiliser la liaison de constructeur, nous devons activer explicitement notre classe de configuration soit avec @EnableConfigurationProperties, soit avec @ConfigurationPropertiesScan .

9. Conclusion

Dans cet article, nous avons exploré l' annotation @ConfigurationProperties et mis en évidence certaines des fonctionnalités utiles qu'elle fournit, telles que la liaison détendue et la validation de bean.

Comme d'habitude, le code est disponible sur Github.