Liaison et gestion de la configuration avec Spring Boot : @ConfigurationProperties et @PropertySource

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 @Component ou @Configuration sur 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).

Étiquettes: Spring Boot Java configuration management Spring Framework

Publié le 29 juillet à 14h07