Mise à jour du 23/04/2026 : description plus précise de la résolution des propriétés dans Spring

Spring Boot possède une fonctionnalité d’externalisation de la configuration extrêmement riche permettant de configurer les applications à partir de sources très variées (fichiers dans le classpaths, fichiers externes, variables d’environnement, propriétés systèmes, arguments de la ligne de commande, Servlet context …). La multiplicité des sources peut parfois entraîner de la confusion et aboutir à une mauvaise configuration de l’application. D’autant plus que la propriété mal configurée peut être difficile à identifier si elle empêche l’application de démarrer et d’afficher suffisament de logs pour diagnostiquer le problème.
Ce billet présente une solution à ce problème : l’affichage précoce des propriétés utilisées dans l’application avec la valeur réellement prise en compte par Spring Boot et une indication sur la source dont provient de cette valeur. Afin de présenter une solution pour l’affichage des propriétés disponibles dans l’application et leur origine, nous verrons :
- Comment Spring Boot gère les propriétés et résout leurs valeurs ?
- Comment connaître les clés de propriété disponibles dans une application Spring ?
- Comment connaître l’origine de la valeur d’une propriété avec l’API
Originde Spring Boot ? - Comment afficher les propriétés au plus tôt dans le cycle de vie de l’application ?
Comment Spring Boot gère les propriétés et résout leurs valeurs ? Link to heading
La gestion des properties côté Spring Framework se fait essentiellement grâce à 3 classes / interfaces :
PropertySource<T>qui sert à incarner une source de paires clé/valeur (les propriétés) qui partagent une origine commune (fichier de propriétés, arguments de la ligne de commande, variables d’environnement).Treprésente le type sous-jacent qui contient les propriétés (par exemplejava.util.Properties,java.util.Map).PropertySourcesqui aggrège lesPropertySource<T>en une collection unique pour un environnement Spring donné. En pratique, c’est l’implémentationMutablePropertySourcesqui est utiliséePropertyResolverqui permet la résolution des propriétés pour un environnement Spring au sein d’une instance dePropertySourcesdans le respect de la préséance entre lesPropertySource.
Le diagramme ci-dessous présente les 4 classes / interfaces citées ci-dessus avec les liens existants entre eux et l’environnement (seuls les composants des objets pertinents pour la compréhension sont représentés) :
Le diagramme ci-dessous présente la hiérarchie des classes de PropertySources (seuls
les composants des objets pertinents pour la compréhension sont représentés) :
Le diagramme ci-dessous présente la hiérarchie des classes de PropertyResolver (seuls
les composants des objets pertinents pour la compréhension sont représentés) :
Le diagramme ci-dessous présente la hiérarchie des classes de PropertySource<T> (seuls
les composants des objets pertinents pour la compréhension sont représentés) :
Détection des propriétés au démarrage de Spring Boot Link to heading
Lors du lancement d’une application Spring Boot,
la plupart des sources de propriétés sont lues et leurs propriétés chargées en mémoire au moment de la création de
l’Environment qui est effectuée par la méthode org.springframework.boot.SpringApplication#prepareEnvironment.
En outre les sources de propriétés sont également référencées par un objet MutablePropertySources
dans un ordre correct permettant de suivre les règles de préséance définies au niveau de
la fonctionnalité d’externalisation de la configuration
La création de l’environnement intervenant très tôt dans le cycle de vie de l’application (c’est la première chose qui est faite après le chargement des listeners), les propriétés ordonnées et leurs valeurs sont donc disponibles avant le chargement du contexte applicatif et leur utilisation par les beans.
Principe de résolution des propriétés dans Spring selon les règles de préséance Link to heading
Nous avons vu qu’au moment du chargement des propriétés, les sources de propriétés sont référencées par un
objet MutablePropertySources dans un ordre bien précis permettant de répondre à la préséance définie dans
au niveau de la fonctionnalité d’externalisation de la configuration : les PropertySource sont
référencées dans une collection ordonnéee par priorité décroissante.
Dans Spring comme dans Spring Boot, lorsque les implémentations de PropertyResolver
doivent résoudre la valeur d’une propriété via les méthodes getProperty(String key)
ou getProperty(String key, Class<T> targetType), elles vont donc itérer sur la collection ordonnée
par priorité décroissante de PropertySource
de l’objet MutablePropertySources associé et retourner la valeur dès qu’une PropertySource fournit une valeur pour la
clé de propriété demandée. Ainsi la préséance dans la résolution des propriétés est respectée.
Objets spécifiques à Spring Boot pour représenter les propriétés Link to heading
Spring Boot utilise des objets spécifiques pour représenter les propriétés et leurs sources afin d’apporter des
fonctionnalités supplémentaires : les propriétés sont représentées par des instances de la classe ConfigurationProperty.
Cette classe représente une propriété avec les données suivantes :
- le nom de la propriété sous forme d’une instance de
ConfigurationPropertyName - la valeur de la propriété
- la source d’appartenance de la propriété
- l’origine de la propriété (et de sa valeur) sous forme d’une instance de
Origin
L’utilisation de la classe ConfigurationPropertyName
pour représenter le nom de la propriété permet à Spring Boot de proposer des fonctionnalités plus riches pour la gestion
des propriétés comme le relaxed binding pour les ConfigurationProperties
L’utilisation de Origin
permet d’indiquer l’origine d’une propriété : c’est cette donnée qui permettra de répondre au problème de l’affichage
précoce des propriétés avec leur valeur et leur origine.
Nous utiliserons donc les équivalents Spring Boot des objets décrits ci-dessus utilisés par Spring pour gérer les propriétés. Les algorithmes de résolution des valeurs des propriétés suivent les mêmes implémentations, la différence tient dans les métadonnées qui enrichissent les objets Spring Boot.
Correspondance entre les objets de Spring et Spring Boot pour la représentation des propriétés et de leurs sources Link to heading
| Spring core | Spring Boot | |
|---|---|---|
| Nom (clé) de la propriété | String | ConfigurationPropertyName |
| Paire clé valeur | Variable suivant les cas (par exemple Map.Entry) | ConfigurationProperty |
| Source de propriétés | PropertySource | ConfigurationPropertySource |
| Ensemble de sources de propriétés | PropertySources (en réalité MutablePropertySources) | SpringConfigurationPropertySources |
| Résolveur de propriétés pour un ensemble de sources | PropertyResolver | ConfigurationPropertySourcesPropertyResolver |
NB : la classe
SpringConfigurationPropertySourcesn’est pas publique et l’accès aux différentesConfigurationPropertySourced’un environnement se fait grâce à la méthode statiqueConfigurationPropertySource#from
Comment connaître les clés de propriété disponibles dans une application Spring avec les PropertySource ?
Link to heading
Afin d’afficher les propriétés disponibles dans une application Spring Boot, il est nécessaire de pouvoir lister les clés
disponibles à partir d’un Environment Spring. Cela peut se faire en deux étapes :
- Lister les
PropertySourcede l’Environment - Pour chaque
PropertySource, lister les noms de propriétés
ConfigurableEnvironment pour énumérer les PropertySource de l’Environment
Link to heading
ConfigurableEnvironment
permet d’avoir accès aux objets PropertySource référencés par l’environnement via la méthode
ConfigurableEnvironment#getPropertySources qui retourne un objet de type MutablePropertySources (qui permet d’itérer
sur les PropertySource qu’il référence). La quasi-totalité des Environment dans Spring sont des ConfigurableEnvironment :
il suffit donc de récupérer l’instance d’Environment de l’application et de la caster en ConfigurableEnvironment.
L’instance d’Environment quant à elle peut être retrouvée via ApplicationContext#getEnvironment dès lors que l’application est initialisée
ou bien de manière bien plus précoce via ApplicationEnvironmentPreparedEvent dans le cas d’une application Spring Boot.
EnumerablePropertySource pour lister les noms de propriétés
Link to heading
EnumerablePropertySource
permet d’énumérer les noms des propriétés qui composent la source grâce à la méthode
getPropertyNames. La plupart des sources de propriétés (fichiers, propriétés systèmes,
arguments de la ligne de commande …) sont du type EnumerablePropertySource et permettent donc de connaître
les propriétés qu’elles apportent au contexte de l’application. Les sources de propriétés non énumérables comme
JndiPropertySource restent minoritaires.
Bloc de code résumant les éléments présentés dans cette section Link to heading
public Set<String> findAllPropertyKeys(ApplicationContext applicationContext) {
return ((ConfigurableEnvironment) applicationContext.getEnvironment()).getPropertySources().stream()
.filter(EnumerablePropertySource.class::isInstance)
.map(EnumerablePropertySource.class::cast)
.map(EnumerablePropertySource::getPropertyNames)
.flatMap(Arrays::stream)
.collect(Collectors.toSet());
}
Comment connaître l’origine de la valeur d’une propriété avec l’API Origin de Spring Boot ?
Link to heading
Lorsqu’une même propriété (même clé) est présente dans plusieurs sources de propriétés à la fois, sa valeur sera définie en suivant les règles de préséance définies au niveau de la fonctionnalité d’externalisation de la configuration. Dans certaines situations, la multiplicité des sources ainsi que la flexibilité offerte sur les noms de propriétés par Spring peut rendre délicate la détermination de la valeur qui sera vraiment utilisée par Spring pour une propriété. C’est pourquoi il peut être utile de déterminer l’origine de la valeur d’une propriété.
L’API Origin de Spring Boot
Link to heading
Comme vu ci-dessus, l’origine d’une propriété
se représente dans Spring Boot à travers une instance de l’interface org.springframework.boot.origin.Origin
associée à une ConfigurationProperty.
L’instance d’Origin est susceptible de fournir une information sur l’origine de la valeur incarnée par ConfigurationProperty#value
via par exemple un appel à Origin#toString. Ce qui donnerait une sortie de la forme :
class path resource [application.properties] - 2:29
La sortie donnée en exemple indique que la valeur vient du fichier application.properties trouvé par Spring Boot dans le
classpath et se trouve ligne 2, colonne 29 (la colonne indique le premier caractère de la valeur de la propriété après
le = et tout espace qui le suivrait).
Accès à l’Origin d’une propriété dans une source donnée
Link to heading
Comme indiqué dans le tableau ci-dessus,
c’est un objet ConfigurationPropertySource
qui permet d’accéder aux propriétés d’une PropretySource de Spring sous forme de ConfigurationProperty.
Cela peut se faire grâce à la méthode ConfigurationPropertySource#getConfigurationProperty en lui pasant une instance deConfigurationPropertyName
(nom de la propriété).
L’objet ConfigurationPropertySource associé à une source de propriétés quant à lui peut être obtenu via
un appel à ConfigurationPropertySource#from
Quant à l’instance de ConfigurationPropertyName on l’obtient à partir de la factory statique ConfigurationPropertyName#of
avec le nom (la clé) de la propriété en argument.
Comment déterminer la bonne origine Link to heading
Avec le principe énoncé ci-dessus, il y aura autant d’origines pour une propriété que de sources de propriétés
fournissant une valeur pour la clé. Afin de trouver l’origine qui correspond à la valeur retenue par Spring Boot
dans le respect de la préséance entre les PropertySource, il faut suivre le même algorithme de résolution que
celui défini plus haut.
La solution consiste donc à vérifier les objets Origin renvoyés par chaque PropertySource (préalablement
transformée en ConfigurationPropertySource) en respectant l’ordre dans lequel les PropertySource sont stockées
dans l’objet MutablePropertySources associé à l’environnement. Le premier objet Origin non null correspond très
vraisemblablement à l’origine de la valeur qui sera retournée par Spring Boot pour la propriété.
On peut résumer les éléments présentés dans cette section avec le bloc de code suivant Link to heading
public Optional<String> findPropertyOrigin(String key, ApplicationContext applicationContext) {
ConfigurationPropertyName configurationPropertyName = ConfigurationPropertyName.of(key);
Environment environment = applicationContext.getEnvironment();
return ((ConfigurableEnvironment) environment).getPropertySources().stream()
.map(ConfigurationPropertySource::from)
.filter(Objects::nonNull)
.map(configurationPropertySource -> configurationPropertySource.getConfigurationProperty(configurationPropertyName))
.filter(Objects::nonNull)
.map(ConfigurationProperty::getOrigin)
.filter(Objects::nonNull)
.map(Origin::toString)
.findFirst();
}
Comment afficher les propriétés au plus tôt dans le cycle de vie de l’application ? Link to heading
De l’intérêt d’afficher au plus tôt les propriétés utilisées par l’application Link to heading
L’affichage des propriétés dans les applications se fait parfois au cours de l’instanciation d’un bean ou de
l’appel d’une méthode @PostConstruct. Cependant si une propriété mal valorisée fait échouer la définition ou
l’instanciation des beans précédents dans le contexte, il se peut que le bean chargé d’afficher les propriétés
ne soit jamais instancié. Ainsi l’application s’arrête sans indication sur les valeurs des propriétés utilisées.
La méthode qui suit permet de se prémunir de cet affichage tardif des propriétés.
SpringApplicationEvent lié à la création de l’environnement
Link to heading
Lorsqu’une application Spring Boot démarre, la plupart des propriétés sont résolues très tôt dans le cycle de vie de l’application
(voir la documentation sur les listeners spring boot pour avoir une idée du cycle de vie d’une application Spring Boot) :
au moment de la création de l’Environment qui coïncide avec l’émission de l’évènement ApplicationEnvironmentPreparedEvent.
À partir de ce moment, la plupart des sources de propriétés ont été inspectées par Spring Boot et les valeurs des
propriétés résolues : il est donc possible dès ce moment de retrouver les valeurs réellement utilisées pour les propriétés
ainsi que leurs origines.
Récupérer les propriétés et leurs origines à partir de ApplicationEnvironmentPreparedEvent
Link to heading
L’évènement ApplicationEnvironmentPreparedEvent émis par Spring Boot permet de récupérer l’objet Environment juste après sa
création et avant que Spring n’entame d’autres phases du démarrage de l’application. La récupération de l’instance d’Environment
se fait en référençant en tant que listener de l’application Spring Boot
une classe implémentant ApplicationListener<ApplicationEnvironmentPreparedEvent>. L’Environment sera disponible via l’objet
ApplicationEnvironmentPreparedEvent qui sera passé à la méthode onApplicationEvent
Synthèse des codes vus ci-dessous dans une classe permettant l’affichage des propriétés à la création de l’environnement Link to heading
Associé aux autres exemples présentés plus hauts, voici ce que donnerait par exemple la classe principale d’une application qui affiche dès la création de son environnement ses propriétés, leurs valeurs et leurs origines :
package poc;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyNameException;
import org.springframework.boot.origin.Origin;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
@SpringBootApplication
public class Main {
void main(String[] args) {
new SpringApplicationBuilder(Main.class)
.listeners((ApplicationListener<ApplicationEnvironmentPreparedEvent>) Main::onApplicationEvent)
.build().run(args);
}
private static void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
final ConfigurableEnvironment configurableEnvironment = event.getEnvironment();
final List<ConfigurationPropertySource> configurationPropertySources = extractConfigurationPropertySources(configurableEnvironment);
findAllPropertyKeys(configurableEnvironment)
.map(key -> {
String value = configurableEnvironment.getProperty(key);
Optional<String> origin = findPropertyOrigin(key, configurationPropertySources);
return key + " = " + value + origin.map(" ### FROM "::concat).orElse("");
})
.forEach(IO::println);
}
private static List<ConfigurationPropertySource> extractConfigurationPropertySources(ConfigurableEnvironment configurableEnvironment) {
return configurableEnvironment.getPropertySources().stream()
.map(ConfigurationPropertySource::from)
.filter(Objects::nonNull)
.toList();
}
private static Stream<String> findAllPropertyKeys(ConfigurableEnvironment configurableEnvironment) {
return configurableEnvironment.getPropertySources().stream()
.filter(EnumerablePropertySource.class::isInstance)
.map(EnumerablePropertySource.class::cast)
.map(EnumerablePropertySource::getPropertyNames)
.flatMap(Arrays::stream)
.distinct();
}
private static Optional<String> findPropertyOrigin(String key, List<ConfigurationPropertySource> configurationPropertySources) {
try {
final ConfigurationPropertyName configurationPropertyName = ConfigurationPropertyName.of(key);
return configurationPropertySources.stream()
.map(configurationPropertySource -> configurationPropertySource.getConfigurationProperty(configurationPropertyName))
.filter(Objects::nonNull)
.map(ConfigurationProperty::getOrigin)
.filter(Objects::nonNull)
.map(Origin::toString)
.findFirst();
} catch (InvalidConfigurationPropertyNameException e) {
return Optional.empty();
}
}
}
NB : Même s’il est très probable que l’origine et la valeur affichées pour la propriété soit celles qui seront prises en compte par Spring Boot, cela n’est pas garanti : certaines sources de propriétés sont résolues lors de la création du contexte applicatif (en tant que Beans Spring ou par des Beans Spring) donc après la création de l’environnement et l’exécution du code ci-dessus. C’est par exemple le cas des fichiers référencés par des annotations
@PropertySource, des propriétés introduites par les méthodes annotées@DynamicPropertySourcedans les tests ou des propriétés venant des paramètres d’initialisation d’un conteneur de servlets.
Utiliser une librairie tierce pour afficher systématiquement le contenu et l’origine des propriétés dans une application Spring Boot Link to heading
En intégrant le code ci-dessus dans une application Spring Boot, on bénéficiera de l’affichage précoce des propriétés avec leurs valeurs et leurs origines. Cependant, il restera à gérer d’autres aspects :
- limiter les propriétés qui seront affichées (si l’affichage se révèle trop verbeux)
- les sources utilisées pour récupérer les clés des propriétés (si l’affichage se révèle trop verbeux)
- masquer les secrets
- éviter les exceptions pour les propriétés qui dépendent de placeholders absents
- signaler les sources de propriété non énumérables
- formatter l’affichage pour rendre lisible l’affichage des propriétés et leurs valeurs
- pouvoir désactiver l’affichage
En outre, l’intégration du code au moyen d’un copier-coller est une mauvaise pratique et il est préférable de pouvoir adjoindre le processus d’affichage des propriétés à l’application existante sans en modifier le code, par exemple en ajoutant une dépendance dans le classpath.
C’est ce que propose la librairie Spring-Boot-Properties-Logger qui rend tous les services ci-dessus simplement en l’ajoutant dans les dépendances de l’application et en configurant quelques propriétés pour la paramétrer :
<dependency>
<groupId>io.github.fbibonne</groupId>
<artifactId>boot-properties-logger-starter</artifactId>
<version>2.3.0</version>
</dependency>
# Affichage des propriétés Spring ainsi que les propriétés du domaine com.example sur la base des préfixes des clés
properties.logger.prefix-for-properties=debug, trace, info, logging, spring, server, management, springdoc, properties, com.example
La librairie Spring-Boot-Properties-Logger gère l’affichage des propriétés, de leurs valeurs et de leurs origines, mais aussi des sources de propriétés détectées. Les sources de propriétés détectées ainsi que les propriétés dont les secrets sont à masquer sont aussi paramétrables.
Afin de pouvoir s’enregistrer comme listener auprès de l’application Spring Boot sans modifier le code applicatif, la librairie
Spring-Boot-Properties-Logger utilise le mécanisme des fichiers spring.factories.
Un fichier spring.factories contenant le nom du listener fourni par la librairie implémentant org.springframework.context.ApplicationListener
se trouve dans le dossier META-INF du jar de la librairie : c’est par ce truchement que le listener en question
est passé à l’application Spring Boot et appelé lorsque l’évènement ApplicationEnvironmentPreparedEvent est émis.