Spring Boot et Spring Cloud
Configuration Spring Boot : properties, YAML, profils et @ConfigurationProperties
Débutant10 min de lectureMis à jour
Cette page a été traduite de l'anglais avec l'aide de l'IA, puis vérifiée par rapport à l'original. Lire la version anglaise
Vous saurez configurer un service Spring Boot 3 pour plusieurs environnements sans le reconstruire : choisir entre properties et YAML, comprendre quelle source l'emporte en cas de conflit, activer correctement les profils, lier les paramètres à des classes de configuration immuables et validées, et garder les secrets hors du jar.
Ce que vous allez apprendre
- Classer les sources de propriétés de Spring Boot et prédire quelle valeur l'emporte quand une même clé est définie deux fois
- Écrire une configuration propre à chaque profil avec des fichiers application-{profile} et du YAML multi-documents
- Lier la configuration à des records @ConfigurationProperties immuables et validés
- Utiliser le relaxed binding pour que variables d'environnement, clés YAML et arguments de ligne de commande désignent la même propriété
- Importer des fichiers de configuration externes et garder les identifiants hors de l'artefact packagé
Un service qu'il faut reconstruire pour changer un port, une URL de base de données ou un feature flag ne peut pas être promu de la préproduction à la production sous la forme du même artefact, alors que le même artefact est toute la raison d'être d'un pipeline de déploiement. La réponse de Spring Boot, c'est la configuration externalisée : un seul jar immuable, de nombreuses sources de propriétés empilées les unes sur les autres au démarrage, avec un ordre défini pour résoudre les conflits. Cette page couvre cet ordre, les formats de fichiers, les profils, le binding typé, et les habitudes qui évitent que la configuration ne se transforme en séance de débogage.
D'où viennent les propriétés
Boot construit un unique Environment à partir de nombreux PropertySource. Quand la même clé apparaît dans plusieurs d'entre eux, celui qui a la priorité la plus élevée l'emporte. La liste ci-dessous va de la priorité la plus basse à la plus haute et reprend les sources que vous rencontrerez en pratique (la documentation de référence donne la liste complète) :
- Les propriétés par défaut définies dans le code avec
SpringApplication.setDefaultProperties - Les annotations
@PropertySourcesur les classes@Configuration - Les fichiers de config data :
application.properties/application.yml, puis leurs variantes propres à un profil - Un
RandomValuePropertySource(random.int,random.uuid) - Les variables d'environnement du système d'exploitation
- Les propriétés système Java (
-Dserver.port=9090) - Les attributs JNDI, ainsi que les paramètres du servlet context et du servlet config
SPRING_APPLICATION_JSON(du JSON en ligne dans une variable d'environnement ou une propriété système)- Les arguments de ligne de commande (
--server.port=9090) propertiessur@SpringBootTest,@TestPropertySourceet@DynamicPropertySourcedans les tests- Les paramètres globaux de DevTools sur le poste du développeur
Deux conséquences méritent d'être bien assimilées. D'abord, tout ce qui est défini en ligne de commande ou dans l'environnement l'emporte sur ce qui se trouve dans le jar, ce qui est exactement ce dont on a besoin en exploitation. Ensuite, @PropertySource est chargé si tard dans le processus qu'il ne peut pas influencer les décisions précoces comme la mise en place du logging ou spring.profiles.active ; c'est une fonctionnalité de compatibilité, pas le mécanisme principal.
Au sein de la couche config data, les fichiers sont eux aussi ordonnés. Boot cherche application.* aux emplacements suivants, les derniers surchargeant les premiers :
- la racine du classpath
- le package
/configdu classpath - le répertoire de travail courant
- un sous-répertoire
/configdu répertoire de travail, ainsi que ses enfants directs
Ainsi, un fichier déposé à côté du jar dans ./config/application.yml surcharge celui qui est packagé à l'intérieur, sans aucune option. spring.config.name change le nom de base et spring.config.location remplace entièrement le chemin de recherche (spring.config.additional-location le complète). Tous deux doivent être fournis tôt, en argument de ligne de commande ou en variable d'environnement, car ils sont lus avant les fichiers.
Properties ou YAML
Les deux formats expriment le même arbre. Comparez :
server.port=8081
spring.datasource.url=jdbc:postgresql://localhost:5432/shop
spring.datasource.username=shop
spring.jpa.open-in-view=false
app.allowed-origins[0]=https://shop.example.com
app.allowed-origins[1]=https://admin.example.com
server:
port: 8081
spring:
datasource:
url: jdbc:postgresql://localhost:5432/shop
username: shop
jpa:
open-in-view: false
app:
allowed-origins:
- https://shop.example.com
- https://admin.example.com
YAML l'emporte en lisibilité dès qu'apparaissent imbrications et listes, et il autorise plusieurs documents dans un même fichier, séparés par ---. En contrepartie, il est sensible à l'indentation et réserve quelques surprises à l'analyse : yes/no/on/off non entourés de guillemets deviennent des booléens avec certains parseurs YAML, 01 peut être lu comme un nombre octal, et une valeur qui contient deux-points doit être mise entre guillemets. Boot s'appuie sur SnakeYAML, qui doit être présent sur le classpath ; les starters l'incluent. Notez aussi que les fichiers YAML ne peuvent pas être chargés via @PropertySource.
Les placeholders fonctionnent dans les deux formats : app.base-url=https://${app.host}:${server.port} est résolu sur l'ensemble de l'Environment, et ${MY_VAR:default} fournit une valeur de repli.
Les profils
Un profil est une tranche nommée de configuration et de beans. Activez-en un ou plusieurs avec spring.profiles.active=prod (propriété, variable d'environnement SPRING_PROFILES_ACTIVE ou --spring.profiles.active=prod). Quand aucun profil n'est actif, c'est le profil default qui l'est.
Les fichiers propres à un profil suivent la convention de nommage application-{profile}.yml, se placent aux mêmes emplacements que application.yml et le surchargent. Si plusieurs profils sont actifs, le dernier de la liste l'emporte. On obtient le même effet dans un seul fichier YAML :
spring:
application:
name: shop
jpa:
open-in-view: false
---
spring:
config:
activate:
on-profile: local
datasource:
url: jdbc:h2:mem:shop
logging:
level:
org.hibernate.SQL: DEBUG
---
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:postgresql://db.internal:5432/shop
server:
shutdown: graceful
La clé spring.config.activate.on-profile a remplacé l'ancienne clé spring.profiles dans Boot 2.4. Les expressions de profil sont autorisées : on-profile: "prod & eu" ou "!local".
Les profils conditionnent aussi les beans. @Profile("local") sur une @Configuration ou un @Bean ne l'enregistre que si ce profil est actif ; @Profile("!prod") est la façon habituelle de garder une fausse passerelle de paiement hors de la production. Utilisez-les avec parcimonie : un service dont le graphe de beans diffère fortement d'un environnement à l'autre est un service que vous n'avez pas vraiment testé avant de le déployer.
Les groupes de profils permettent à un profil de se développer en plusieurs : spring.profiles.group.prod=prod-db,prod-mq signifie qu'activer prod active aussi les deux autres. spring.profiles.include ajoute des profils sans condition. Aucun des deux ne peut figurer dans un document propre à un profil, pour la raison d'ordonnancement expliquée dans la FAQ.
Une organisation courante et raisonnable : un application.yml de base avec tout ce qui est partagé, un petit fichier de profil par environnement qui ne contient que les différences, le tout versionné, et les secrets fournis de l'extérieur (voir plus bas). Les noms d'environnement comme local, test, staging et prod sont des conventions, pas des mots-clés ; Boot ne s'intéresse qu'à la chaîne.
Binding typé avec @ConfigurationProperties
@Value("${app.timeout}") convient pour un ou deux paramètres. Au-delà, il disperse des clés sous forme de chaînes dans tout le code, sans validation ni assistance de l'IDE. @ConfigurationProperties lie tout un préfixe à un objet :
package com.example.shop.config;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Positive;
import java.time.Duration;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.validation.annotation.Validated;
@Validated
@ConfigurationProperties(prefix = "app")
public record AppProperties(
@NotEmpty List<String> allowedOrigins,
@Positive int maxPageSize,
Duration requestTimeout, // "30s", "2m", "500ms" all bind
Catalog catalog) {
public record Catalog(boolean cacheEnabled, Duration cacheTtl) {}
}
Pour l'enregistrer, il suffit de l'une de ces deux annotations sur une classe de configuration : @EnableConfigurationProperties(AppProperties.class) pour un contrôle explicite, ou @ConfigurationPropertiesScan sur la classe de l'application pour prendre en compte toutes les classes annotées de l'arborescence de packages. Injectez ensuite AppProperties là où vous en avez besoin, comme n'importe quel autre bean.
Les records apportent gratuitement le binding par constructeur et l'immuabilité. Dans Boot 3, un record, ou une classe dotée d'un unique constructeur avec paramètres, est lié automatiquement via son constructeur ; l'annotation explicite @ConstructorBinding de Boot 2 n'est nécessaire que si une classe possède plusieurs constructeurs. Un JavaBean mutable avec setters fonctionne toujours quand vous avez besoin de valeurs par défaut qui dépendent d'autres valeurs, mais les records doivent rester le choix par défaut.
@Validated active Jakarta Bean Validation sur l'objet lié : un allowedOrigins manquant fait échouer le démarrage avec un message clair qui indique la valeur rejetée et la contrainte. Mieux vaut échouer tôt que découvrir une faute de frappe dans un nom de propriété à la première requête en production.
Ajoutez spring-boot-configuration-processor comme dépendance optionnelle (annotationProcessor avec Gradle) et le build génère META-INF/spring-configuration-metadata.json, qui vous donne l'autocomplétion et la documentation de vos propres clés dans IntelliJ IDEA, Eclipse et VS Code. La Javadoc des composants du record devient la description affichée dans l'IDE.
Relaxed binding
Boot normalise les noms de clés pour qu'une même propriété puisse s'écrire dans le style naturel de chaque source :
| Source | Écriture de app.max-page-size |
|---|---|
| Properties / YAML | app.max-page-size (kebab-case, forme canonique), mais aussi app.maxPageSize ou app.max_page_size |
| Variable d'environnement | APP_MAXPAGESIZE (majuscules, points remplacés par des underscores, tirets supprimés) |
| Propriété système / ligne de commande | app.max-page-size ou app.maxPageSize |
Pour les listes dans les variables d'environnement, les indices deviennent des segments numériques : APP_ALLOWEDORIGINS_0=https://shop.example.com. Le relaxed binding s'applique à @ConfigurationProperties ; @Value est plus strict et attend le nom canonique exact.
Conversion
Le binding comprend les durées (10s, PT10S, ou un simple nombre avec @DurationUnit), les tailles de données (10MB, @DataSizeUnit), les périodes, les enums (sans tenir compte de la casse), File, Resource, InetAddress et tout type doté d'un constructeur String ou d'une méthode valueOf. Les conversions personnalisées s'enregistrent via un bean Converter annoté @ConfigurationPropertiesBinding.
Importer de la configuration et gérer les secrets
spring.config.import ajoute des sources supplémentaires à la couche config data, traitées après le fichier qui les déclare :
spring.config.import=optional:file:./overrides.properties,optional:configtree:/run/secrets/
file:etclasspath:importent des fichiers properties ou YAML ordinaires.optional:évite l'échec du démarrage quand l'emplacement est absent, ce qui est exactement ce qu'il faut pour un fichier de surcharge réservé aux développeurs.configtree:lit un répertoire où le nom de chaque fichier est la clé et son contenu la valeur, c'est-à-dire la structure que produisent Kubernetes et Docker quand ils montent des secrets. Un fichier/run/secrets/db-passworddevient la propriétédb-password.configserver:(avec Spring Cloud Config) etvault:(avec Spring Cloud Vault) récupèrent la configuration depuis un stockage central.
Rien de tout cela n'a sa place dans le jar : ni mots de passe, ni clés d'API, ni certificats privés. Fournissez-les par des variables d'environnement, des fichiers de secrets montés ou un serveur de configuration, et référencez-les depuis application.yml avec des placeholders comme password: ${DB_PASSWORD}. Si vous devez vraiment garder une valeur chiffrée dans un fichier, le préfixe {cipher} de Spring Cloud Config et les intégrations Jasypt existent, mais un gestionnaire de secrets reste le meilleur outil.
Configurer le logging par les propriétés
Le logging est la configuration que la plupart des gens touchent en premier, et Boot expose les cas courants sous forme de propriétés, si bien qu'un fichier de configuration logback est rarement nécessaire :
logging.level.root=INFO
logging.level.com.example.shop=DEBUG
logging.level.org.springframework.web=DEBUG
logging.file.name=/var/log/shop/app.log
logging.pattern.console=%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n
logging.logback.rollingpolicy.max-file-size=50MB
logging.file.name et logging.file.path ont remplacé les clés logging.file et logging.path d'avant la version 2.2 ; les anciens noms apparaissent encore dans des tutoriels et ne fonctionnent plus. Logback est l'implémentation par défaut derrière les starters ; passer à Log4j2 consiste à exclure spring-boot-starter-logging et à ajouter spring-boot-starter-log4j2. Comme le logging est initialisé avant la majeure partie du contexte, les propriétés logging.* doivent se trouver dans un fichier de config data, une variable d'environnement ou la ligne de commande ; un @PropertySource arrive trop tard.
Erreurs fréquentes
Supposer que le fichier de profil complète la liste au lieu de remplacer la valeur. Les listes sont remplacées en bloc : un app.allowed-origins à deux entrées dans application.yml et à une entrée dans application-prod.yml donne une seule entrée en production, pas trois. Les maps, en revanche, sont fusionnées clé par clé.
Définir spring.profiles.active dans application.yml et se demander pourquoi on ne peut pas le surcharger. On le peut : un argument de ligne de commande l'emporte. Le vrai problème, c'est qu'un profil codé en dur dans l'artefact est une chose de plus que les équipes d'exploitation doivent penser à surcharger. Laissez-le non défini dans le fichier packagé et fournissez-le depuis l'environnement.
Utiliser une classe interne sans static pour les propriétés. Le binder ne sait pas instancier une classe interne non statique ; les groupes imbriqués doivent être des classes imbriquées statiques ou des records.
Une faute de frappe dans un nom de propriété. Boot ignore silencieusement les clés inconnues : server.prot=9090 ne fait rien. Activez ignore-unknown-fields = false sur vos propres @ConfigurationProperties, appuyez-vous sur les métadonnées de l'IDE pour les clés de Boot, et privilégiez un petit nombre de préfixes bien nommés.
Transmettre des secrets via SPRING_APPLICATION_JSON ou la ligne de commande. Les deux sont visibles de tout processus capable de lister les arguments ou de lire l'environnement de la JVM ; sur des hôtes partagés, des fichiers montés avec des permissions restreintes sont plus sûrs.
Bonnes pratiques
- Versionnez
application.ymlet tous les fichiersapplication-{profile}.yml; traitez-les comme du code, avec revues et historique. Gardez le fichier de base complet et les fichiers de profil minimaux. - Regroupez les paramètres liés dans un record
@ConfigurationPropertiespar domaine fonctionnel, validez-le, et injectez le record plutôt que des champs@Valueisolés. - Adoptez l'écriture canonique en kebab-case pour les clés dans les fichiers ; c'est celle qu'utilisent les métadonnées et la documentation.
- Documentez chaque clé personnalisée avec de la Javadoc pour que les métadonnées générées l'expliquent dans l'IDE.
- Limitez le nombre de profils et rendez leur signification évidente ; si un profil n'est jamais activé qu'avec un autre, utilisez un groupe au lieu de demander à chacun de se souvenir de la paire.
- Quand le comportement diffère selon l'environnement, préférez une propriété lue par un seul bean au remplacement de beans avec
@Profile.
Points clés à retenir
- Boot fusionne de nombreuses sources de propriétés en un seul
Environment; les arguments de ligne de commande et les variables d'environnement surchargent les fichiers inclus dans le jar, et les fichiers placés à côté du jar surchargent ceux qui sont packagés. - Properties et YAML sont équivalents ; YAML ajoute les fichiers multi-documents et la lisibilité, au prix de l'indentation et de quelques pièges d'analyse.
- Les profils sélectionnent configuration et beans ; les fichiers propres à un profil surchargent le fichier de base, et
spring.profiles.activedoit être fourni de l'extérieur de l'artefact. - Les records
@ConfigurationPropertiesavec@Validatedoffrent une configuration immuable, typée, assistée par l'IDE, qui échoue vite sur les mauvaises valeurs. - Le relaxed binding fait correspondre
app.max-page-size,app.maxPageSizeetAPP_MAXPAGESIZEà la même clé. spring.config.importavecoptional:etconfigtree:est la façon propre d'intégrer des surcharges et des secrets montés sans toucher au jar.
Sources et lectures complémentaires
Questions fréquentes
- Faut-il utiliser application.properties ou application.yml ?
- Les deux formats sont entièrement pris en charge et offrent les mêmes fonctionnalités. YAML se lit plus facilement dès qu'il y a des clés imbriquées et des listes, et il accepte plusieurs documents dans un seul fichier, ce qui est pratique pour les sections propres à un profil. Les fichiers properties sont plus plats, moins sujets aux erreurs d'indentation et mieux pris en charge par certains outils. Choisissez-en un par projet ; si les deux existent sous le même nom, le fichier .properties est prioritaire.
- Pourquoi ma variable d'environnement ne surcharge-t-elle pas application.yml ?
- Vérifiez d'abord le nom : SPRING_DATASOURCE_URL correspond à spring.datasource.url grâce au relaxed binding, mais les segments doivent être séparés par un seul underscore et les tirets du nom de propriété supprimés. Vérifiez ensuite la portée : la variable doit être visible du processus JVM lui-même, pas seulement de votre shell ou d'une étape de build Docker. Enfin, rappelez-vous que les arguments de ligne de commande et les propriétés de test ont une priorité plus élevée que les variables d'environnement.
- Peut-on définir spring.profiles.active dans un fichier propre à un profil ?
- Non. Boot refuse spring.profiles.active et spring.profiles.include dans les fichiers propres à un profil et dans les documents YAML activés par spring.config.activate.on-profile : un profil qui en active d'autres pendant son propre chargement rendrait le résultat dépendant de l'ordre. Utilisez plutôt spring.profiles.group dans le document principal pour définir un profil qui se développe en plusieurs autres.
- Comment voir la valeur finale d'une propriété à l'exécution ?
- Avec Actuator, exposez l'endpoint env et interrogez /actuator/env/your.property.name : il liste toutes les sources de propriétés qui contiennent la clé et indique laquelle l'a emporté. Dans Boot 3, les valeurs sont masquées par défaut ; définissez management.endpoint.env.show-values=when-authorized (ou always, uniquement dans un environnement protégé) pour les voir. Pour une vérification ponctuelle, injectez Environment et journalisez getProperty dans un CommandLineRunner.