L'annotation @ConfigurationProperties dans Spring Boot permet de lier des fichiers de configuration externes (comme application.yml ou application.properties) à des objets Java (POJO). Cette approche offre une alternative plus structurée et typée par rapport à l'utilisation répétitive de l'annotation @Value.
Méthodes d'activation de @ConfigurationProperties
Pour que Spring puisse détecter et injecter les valeurs dans une classe annotée avec @ConfigurationProperties, il existe trois approches principales :
- Scan de composants : Ajouter
@Componentou@Configurationsur la classe de propriétés pour qu'elle soit détectée par le balayage automatique de Spring. - Déclaration via @Bean : Instancier manuellement la classe de propriétés dans une classe de configuration.
- Utilisation de @EnableConfigurationProperties : Déclarer explicitement les classes de propriétés à activer au niveau d'une configuration globale.
Exemple de configuration YAML
Considérons le fichier application.yml suivant définissant plusieurs blocs de configuration :
server:
port: 9090
servlet:
context-path: /api-v1
# Propriétés pour l'infrastructure
infra:
host: "10.0.0.5"
port: 5432
active: true
nodes:
- "node-01"
- "node-02"
# Propriétés pour la facturation
billing:
currency: "EUR"
vat-rate: 20
# Propriétés pour le monitoring
monitoring:
interval: 60
severity: "high"
# Informations système simples
system:
name: "MainCluster"
version: "2.4.0"
Implémentation des classes de propriétés
Voici comment mapper ces sections sur des objets Java. La classe InfraConfig utilise l'injection directe via le scan de composants :
package com.example.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
@ConfigurationProperties(prefix = "infra")
public class InfraConfig {
private String host;
private Integer port = 8080; // Valeur par défaut
private Boolean active = Boolean.FALSE;
private List<String> nodes;
// Getters et Setters obligatoires pour la liaison
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public Integer getPort() { return port; }
public void setPort(Integer port) { this.port = port; }
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
public List<String> getNodes() { return nodes; }
public void setNodes(List<String> nodes) { this.nodes = nodes; }
}
Les classes suivantes seront enregistrées via d'autres méthodes dans la classe de configuration centrale :
// Classe pour la facturation (utilisera @Bean)
public class BillingConfig {
private String currency;
private Integer vatRate;
// Getters/Setters...
}
// Classe pour le monitoring (utilisera @EnableConfigurationProperties)
@ConfigurationProperties(prefix = "monitoring")
public class MonitoringConfig {
private Integer interval;
private String severity;
// Getters/Setters...
}
Configuration centrale et activation
La classe AppGlobalConfig orchestre l'activation des différentes propriétés :
package com.example.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(MonitoringConfig.class)
public class AppGlobalConfig {
@Bean
@ConfigurationProperties(prefix = "billing")
public BillingConfig billingConfig() {
return new BillingConfig();
}
}
Accès aux données via un contrôleur REST
L'utilisation de @Value reste pertinente pour des valeurs isolées, tandis que les objets de configuration sont injectés via @Autowired :
package com.example.controller;
import com.example.config.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.HashMap;
@RestController
public class ConfigController {
@Autowired
private InfraConfig infra;
@Autowired
private BillingConfig billing;
@Autowired
private MonitoringConfig monitoring;
@Value("${system.name}")
private String sysName;
@GetMapping("/inspect/infra")
public Map<String, Object> getInfra() {
Map<String, Object> res = new HashMap<>();
res.put("host", infra.getHost());
res.put("nodes", infra.getNodes());
return res;
}
@GetMapping("/inspect/system")
public String getSystem() {
return "Système : " + sysName;
}
}
Utilisation de @PropertySource pour les fichiers externes
Pour éviter de surcharger le fichier application.yml, il est possible d'extraire des configurations spécifiques dans un fichier dédié, par exemple external-api.properties situé dans le répertoire resources :
api.key=AIzaSyA4_B5
api.timeout=5000
api.endpoint=https://api.service.com
L'annotation @PropertySource permet de spécifier l'emplacement de ce fichier au sein d'un composant :
package com.example.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource(value = "classpath:external-api.properties")
@ConfigurationProperties(prefix = "api")
public class ExternalApiConfig {
private String key;
private int timeout;
private String endpoint;
// Getters et Setters...
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public int getTimeout() { return timeout; }
public void setTimeout(int timeout) { this.timeout = timeout; }
public String getEndpoint() { return endpoint; }
public void setEndpoint(String endpoint) { this.endpoint = endpoint; }
}
L'avantage majeur de @ConfigurationProperties par rapport à @Value réside dans le support des types complexes (List, Map), la validation JSR-303 (via @Validated) et le "relaxed binding" (capacité à faire correspondre vat-rate en YAML à vatRate en Java).