1. Quand la recherche par défaut est insuffisante
Si vous entendez parler pour la première fois de spring.config.import et de ses amis, vous pouvez être pris d'une légère panique : « Alors, avant, tout était faux ? » Pas de panique. Dans la plupart des cas, Spring Boot trouve vraiment lui-même application.yml et application-{profile}.yml aux endroits attendus, et c'est une bonne magie de base qui vous épargne bien des soucis. Mais parfois, vous ne voulez pas de magie, mais un scénario contrôlé.
Tant que le fichier externe s'appelait application.yml ou application-postgres.yml et se trouvait à l'endroit habituel, près du lancement, Spring Boot pouvait souvent le trouver tout seul. Mais dès que le nom devient arbitraire — par exemple runtime.yml — ou que le chemin cesse d'être standard, la recherche automatique ne suffit plus. C'est là qu'interviennent spring.config.import, spring.config.location et spring.config.additional-location : ils ne remplacent pas les application*.yml habituels, mais servent à gérer explicitement les fichiers non standards et les chemins de recherche.
Imaginez deux situations typiques de la vie d'un service pédagogique. Dans la première, vous voulez garder dans le jar une base de référence claire qui se lance toujours, et à côté avoir un petit fichier runtime.yml qui peut être présent ou non, et s'il n'est pas là, l'application ne doit pas planter. Dans la seconde, vous voulez dire : « Voici où se trouvent mes configurations, et uniquement là. Ne cherche pas dans toutes les poches de la veste et dans chaque config/. » Et ce sont précisément ces scénarios que nous offrent trois outils : spring.config.import, spring.config.location et spring.config.additional-location.
Pour ne pas se perdre, gardons en tête une métaphore simple. Par défaut, Spring Boot se comporte comme une personne qui cherche ses clés d'appartement selon un itinéraire habituel : « dans la poche, sur la table, dans le sac à dos ». additional-location, c'est quand vous ajoutez un endroit supplémentaire à l'itinéraire : « et aussi dans le tiroir de l'entrée ». location, c'est quand vous dites : « stop, on cherche uniquement dans le tiroir, nulle part ailleurs ». Et import, c'est quand vous ouvrez un carnet « où je range mes clés » et qu'à l'intérieur du carnet vous écrivez : « et vérifie aussi l'enveloppe dans le dossier ».
2. spring.config.import : connecter un fichier
spring.config.import est souvent perçu comme « une autre façon de connecter une configuration », mais il est plus utile de le voir comme coller un document de configuration à un autre. Vous prenez votre fichier principal application.yml (ou un fichier spécifique à un profil) et vous dites : « S'il existe tel fichier externe, lis-le aussi ». Dans le monde des conteneurs, c'est pratique car vous ne reconstruisez pas l'image, vous donnez simplement à l'application une couche supplémentaire de paramètres.
Où déclarer import
Il est très important de ne pas se tromper sur « quoi écrire où ». spring.config.import peut être déclaré à l'intérieur d'un fichier de configuration, c'est-à-dire directement dans application.yml (qui se trouve dans src/main/resources et se retrouve à l'intérieur du jar). On obtient alors un effet agréable : l'image et le jar contiennent toujours une base de référence, et le fichier externe devient une « surcouche optionnelle ».
Imaginons que notre service possède déjà une propriété app.export-dir (nécessaire pour exporter un catalogue en CSV). Dans le jar, nous voulons avoir une valeur par défaut sûre, par exemple /app/exports, et sur une machine de développement spécifique, nous voulons la redéfinir sur quelque chose comme /workspace/catalog-exports.
Le fichier application.yml de base peut alors ressembler à ceci :
# src/main/resources/application.yml
spring:
config:
import: "optional:file:/config/runtime.yml" # Connecte optionnellement la couche externe (si le fichier n'existe pas, le démarrage continue)
app:
export-dir: /app/exports # Valeur de base « depuis l'intérieur du jar », valeur par défaut sûre
Notez deux choses. Premièrement, nous ne rendons pas le fichier externe obligatoire. Deuxièmement, nous utilisons explicitement file:/... et un chemin fixe à l'intérieur du conteneur. Il ne s'agit pas de la mécanique de fichiers du conteneur, mais du fait que l'application s'attend à voir un fichier à un chemin spécifique.
Ici, /config/runtime.yml n'est plus le fichier externe application.yml habituel que Boot reconnaît par son nom. Nous avons délibérément choisi un nom arbitraire et un chemin fixe, donc le fichier est connecté explicitement.
Et le fichier externe (qui peut par exemple se trouver « près du lancement » ou « dans le conteneur au chemin indiqué ») sera très court :
# /config/runtime.yml (fichier externe)
app:
export-dir: /workspace/catalog-exports # Redéfinition « pour cette machine/lancement »
server:
port: 9090 # Exemple : localement nous voulons écouter sur un autre port
L'idée est que le fichier externe ne contient que ce qui concerne réellement « cette machine / ce lancement », sans copier toute la configuration.
optional: et le lancement sans fichier
Avec le préfixe optional:, tout est simple, mais les conséquences sont énormes. Sans optional:, Spring Boot considère que la ressource importée doit exister, et si ce n'est pas le cas, le démarrage peut échouer. Pour un projet pédagogique, c'est presque toujours gênant : vous voulez que la base de référence se lance « prête à l'emploi », sans que l'étudiant doive d'abord créer des fichiers secrets à des endroits secrets.
C'est pourquoi, dans notre cours, la bonne discipline est la suivante : si le fichier importé est une « couche optionnelle », alors dans import, on utilisera presque toujours optional:.
spring:
config:
import: "optional:file:/config/runtime.yml"
Le sens ici est très humain : « Essaie de lire, mais si ça ne marche pas, ne t'offense pas et démarre avec ce que tu as ».
Chemins fixes et relatifs dans import
Maintenant, un point subtil qui est à l'origine de la plupart des « fantômes » de configuration. Il existe deux styles courants de chemins pour spring.config.import : l'emplacement fixe et relatif (relatif au fichier qui a déclaré l'import). Dans notre vocabulaire, nous les appelons « fixed location » et « import-relative location ».
L'emplacement fixe, c'est quand vous indiquez explicitement où se trouve le fichier, et le chemin ne dépend pas de l'endroit où se trouve la configuration qui importe. Par exemple :
spring:
config:
import: "optional:file:/config/runtime.yml"
C'est une bonne option pour un conteneur : vous avez convenu à l'avance que les configurations (si elles sont externes) se trouvent, disons, dans /config.
L'import relatif, c'est quand vous écrivez un chemin sans file:/classpath: explicite et sans /... absolu, et alors Spring Boot le considère comme « un fichier à côté » (relatif au document de configuration actuel). Par exemple :
spring:
config:
import: "optional:extra/runtime.yml"
Pourquoi est-ce utile ? Pour une « approche par dossier ». Imaginez que vous stockez un ensemble de configurations externes dans un même répertoire, par exemple :
config/
├── application.yml
└── extra/
└── runtime.yml
Alors, il est pratique d'écrire un import relatif à l'intérieur de config/application.yml — et toute la structure devient portable en un bloc : vous copiez le dossier config/ sur une autre machine, et les chemins ne se cassent pas.
Il est important de ne pas essayer de deviner « comment Boot calcule exactement ce chemin » par intuition. La meilleure habitude pour un débutant est de toujours se poser la question : « Est-ce que je veux que le chemin soit lié à un endroit spécifique (fixed), ou est-ce que je veux qu'il soit lié à la configuration actuelle (relative) ? » Si vous vous répondez à cela, 80 % de la confusion disparaît.
Mini-démo : vérifier la redéfinition
Pour ne pas transformer le sujet en théorie sur YAML, il est utile d'avoir dans le projet un petit « capteur » qui montre quelle valeur a été obtenue. Le capteur le plus simple est de journaliser app.export-dir au démarrage.
Supposons que nous ayons des propriétés de configuration :
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app") // Lie les propriétés de la forme app.* à ce type
public record AppProperties(String exportDir) { } // exportDir <- app.export-dir (liaison assouplie)
Supposons que AppProperties soit déjà enregistré comme type @ConfigurationProperties pour app.* ; ici, il n'est nécessaire que comme conteneur lisible des valeurs finales.
Et un petit log au démarrage :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class StartupLoggingConfig {
private static final Logger log = LoggerFactory.getLogger(StartupLoggingConfig.class);
@Bean
ApplicationRunner logExportDir(AppProperties props) { // Le bean s'exécute au démarrage de l'application
return args -> log.info("app.export-dir = {}", props.exportDir()); // {} — placeholder SLF4J, la valeur provient de la configuration finale
}
}
Si ce setup n'existe pas dans le projet, ajoutez un scan ou un enregistrement normal de @ConfigurationProperties (@ConfigurationPropertiesScan ou @EnableConfigurationProperties).
Ensuite, vous associez mentalement : si le runtime.yml externe existe et est importé, vous verrez /workspace/catalog-exports dans les logs. S'il n'existe pas, vous verrez la valeur par défaut /app/exports. Aucune magie, on vérifie simplement « ce qui a été réellement obtenu ».
3. spring.config.location : uniquement les chemins spécifiés
spring.config.location est un outil qui ressemble à « un autre chemin pour connecter des configurations », mais dans son sens, il est beaucoup plus strict. Il dit à Spring Boot : « Voici la liste des endroits où se trouve la configuration. Utilise-les comme chemin de recherche principal ». Et si vous avez l'habitude que Boot trouve toujours application.yml dans le classpath et encore quelques répertoires, avec location, vous pouvez facilement désactiver accidentellement les valeurs par défaut habituelles.
Comment location remplace les valeurs par défaut
Par « emplacements par défaut », nous entendons l'ensemble standard où Boot cherche habituellement application.yml et application-{profile}.yml : à l'intérieur du jar (classpath) et à côté du lancement (répertoire courant, config/, etc.). Lorsque vous utilisez spring.config.location, c'est comme si vous disiez : « Ne cherche pas la configuration selon les règles standard. Je t'indique moi-même où elle se trouve ».
D'où la principale conclusion pratique. spring.config.location est un outil puissant mais dangereux pour un débutant. Il est utile lorsque vous voulez exactement contrôler l'environnement et que vous comprenez ce que vous faites, mais il peut conduire à une situation : « Pourquoi l'application a soudainement cessé de voir application.yml que j'ai placé dans src/main/resources ? » La réponse sera désagréablement simple : parce que vous avez vous-même demandé à Boot de ne pas y regarder.
Dans un projet pédagogique, de tels « tirs dans le pied » sont particulièrement ennuyeux, car vous perdez la base de référence du lancement. Par conséquent, dans ce cours, nous considérons spring.config.location comme un outil « sur mesure », et non comme un paramètre « au cas où ».
Comment définir location de l'extérieur
Maintenant, une mécanique importante, facile à comprendre au niveau pratique. Pour que Spring Boot trouve la configuration, il doit d'abord comprendre où la chercher. Cela signifie que les paramètres « où chercher » doivent arriver avant les configurations elles-mêmes. Par conséquent, spring.config.location (et spring.config.additional-location) sont définis comme :
- variable d'environnement (par exemple, SPRING_CONFIG_LOCATION),
- propriété système JVM (-Dspring.config.location=...),
- argument de ligne de commande (--spring.config.location=...).
Essayer de définir spring.config.location à l'intérieur de application.yml, que Boot n'a pas encore trouvé, est une boucle logique : « prends la carte qui se trouve dans le tiroir que tu trouves grâce à la carte ».
Un exemple avec -D ressemble à :
# Propriété système JVM : définit le chemin de recherche avant même la lecture de application.yml
java -Dspring.config.location=optional:file:/config/ -jar app.jar
Et sous forme d'argument d'application :
# Argument d'application : effet identique, mais lu comme paramètre de lancement
java -jar app.jar --spring.config.location=optional:file:/config/
Dans un conteneur, c'est la même chose, mais c'est souvent plus pratique via les variables d'environnement (rappel de la liaison assouplie du jour précédent) :
# Variable d'environnement : souvent le moyen le plus pratique pour les conteneurs
SPRING_CONFIG_LOCATION=optional:file:/config/
Le point clé ici n'est pas la syntaxe, mais l'idée d'ingénierie : ces propriétés sont lues très tôt, elles doivent donc être disponibles avant que Boot ne commence à « feuilleter » vos fichiers application.
Quand location est approprié
Pour ne pas diaboliser l'outil, indiquons honnêtement le scénario où il est normal. Imaginez que vous exécutez le même jar dans un environnement où vous ne voulez pas du tout utiliser les valeurs par défaut internes (par exemple, parce que vous créez une « configuration strictement définie » et que les valeurs de base dans le jar ne vous sont pas nécessaires). Alors, location vous permet de dire : « Ma configuration se trouve uniquement dans ce répertoire ».
Mais pour notre service pédagogique « mature en conteneur », une approche plus douce convient mieux : conserver la base de référence à l'intérieur du jar et ajouter soigneusement des surcharges externes par-dessus. Et c'est là que spring.config.additional-location l'emporte souvent sur location.
4. spring.config.additional-location : ajouter un chemin de recherche
Si spring.config.location signifie « je réécris les règles du jeu », alors spring.config.additional-location signifie « j'ajoute un chemin de recherche supplémentaire, mais je ne supprime pas le comportement standard ». Pour un projet pédagogique et pour une équipe qui recherche la prévisibilité, c'est généralement un outil plus sûr et plus convivial. Il permet de maintenir deux mondes ensemble : les configurations à l'intérieur du jar comme base de référence et les configurations externes comme une superposition soigneuse.
Différence entre location et additional-location
Au lieu de longues abstractions, disons-le simplement. Si vous utilisez additional-location, le fichier application.yml interne (depuis src/main/resources) continue d'exister comme « squelette » de la configuration, et le répertoire externe devient un « dossier supplémentaire où peuvent également se trouver des fichiers application ». Cela ressemble beaucoup à l'idée de « ne pas tout copier à l'extérieur », mais de n'en sortir que ce qui est nécessaire.
Par exemple, à l'intérieur du jar vous avez :
- application.yml avec les paramètres généraux,
- application-postgres.yml, etc.
Et à l'extérieur, vous voulez redéfinir uniquement server.port et app.export-dir pour une machine spécifique. Alors vous placez un fichier application.yml externe (court) dans le répertoire « supplémentaire » et vous dites à Boot : « Regarde aussi ici ».
Cela se définit à nouveau tôt, « de l'extérieur » :
java -jar app.jar --spring.config.additional-location=optional:file:/config/
ou bien :
java -Dspring.config.additional-location=optional:file:/config/ -jar app.jar
ou via une variable d'environnement :
SPRING_CONFIG_ADDITIONAL_LOCATION=optional:file:/config/
Exemple de structure de fichiers externes
Imaginons une structure propre de répertoire externe. Pas dix fichiers « final-prod-really.yml », mais très simple et clair :
/config/
├── application.yml
└── application-postgres.yml
Alors le fichier externe application.yml pourrait être :
# /config/application.yml (externe)
server:
port: 9090
app:
export-dir: /workspace/catalog-exports
Et si vous voulez externaliser les paramètres postgres (par exemple, parce que différentes machines ont des identifiants ou des hôtes différents), vous pouvez avoir un fichier externe application-postgres.yml :
# /config/application-postgres.yml (externe)
spring:
datasource:
url: jdbc:postgresql://postgres:5432/catalog
username: catalog
password: catalog
L'idée est que Boot traitera ces fichiers de la même manière que les fichiers internes : le profil activé — le fichier de profil participe ; le profil non activé — il ne participe pas. Et tout cela sans reconstruire l'image et sans copier le Dockerfile. Nous restons dans le paradigme « une image, différentes configurations ».
optional: pour additional-location
Comme pour import, vous voulez souvent que le répertoire soit « s'il existe, utilise-le ; s'il n'existe pas, ne casse pas le lancement ». C'est pourquoi il est pratique d'utiliser optional: ici aussi.
--spring.config.additional-location=optional:file:/config/
Ainsi, l'étudiant peut lancer le service sans aucun répertoire externe, et tout fonctionnera avec la configuration de base à l'intérieur du jar. Et lorsque le répertoire externe apparaîtra (en tant qu'outil délibéré, non obligatoire), il commencera à influencer la configuration.
5. Aide-mémoire pour le choix du mécanisme
Lorsque vous êtes confronté à trois noms similaires, le cerveau d'un débutant fait généralement une tentative honnête de « les apprendre par cœur ». Mais dans le développement réel, ce n'est pas la mémoire qui l'emporte, mais la bonne question. Dans notre cas, la question est : « Est-ce que je veux ajouter une petite couche par-dessus la configuration existante, ou est-ce que je veux remplacer complètement le chemin de recherche de la configuration ? »
Pour simplifier le choix, voici un tableau que vous pouvez garder en tête (et ne pas hésiter à y revenir) :
| Mécanisme | Ce que vous dites à Boot | Quand cela convient généralement | Ce qu'il est facile de casser |
|---|---|---|---|
| spring.config.import | « À partir de cette config, lis encore ce fichier » | Quand un seul fichier supplémentaire spécifique est nécessaire (souvent runtime.yml) et que vous voulez le gérer directement depuis application.yml | Si vous oubliez optional: et que le fichier n'existe pas, le démarrage peut échouer |
| spring.config .additional-location | « Cherche les fichiers application aussi dans ce répertoire, mais garde les valeurs par défaut » | Quand un application.yml / application-{profile}.yml externe est nécessaire en superposition sur la base de référence à l'intérieur du jar | Si vous vous attendez à ce que ce soit un « remplacement » alors que c'est un « ajout », vous pouvez être surpris par la précédence finale |
| spring.config .location | « Cherche la configuration uniquement sur ce chemin (en substance, réécris le chemin de recherche) » | Quand vous voulez délibérément désactiver les emplacements par défaut et vous contenter d'une configuration externe | Il est très facile de désactiver la configuration du classpath et de perdre la base de référence |
En bref, comme je l'expliquerais à un ami : import concerne le « connecter un fichier à un autre », additional-location concerne « ajouter un dossier de recherche », location concerne « remplacer toute la liste des dossiers de recherche ».
6. Erreurs courantes
Erreur n°1 : utiliser spring.config.location alors qu'en réalité il suffisait d'« ajouter simplement une autre configuration ».
C'est un classique. La personne veut « glisser un fichier externe », définit spring.config.location, et soudain le fichier application.yml de base à l'intérieur du jar cesse de participer. Résultat : certaines propriétés « disparaissent », et l'application semble cassée. En pratique, la plupart du temps, vous avez besoin de spring.config.additional-location ou d'un spring.config.import soigneux.
Erreur n°2 : oublier optional: et faire du fichier/répertoire externe un bloqueur de démarrage.
Dans un projet pédagogique et dans la plupart des scénarios locaux, le fichier externe est une « superposition pratique », pas une partie obligatoire du lancement. Si on oublie optional:, l'application peut planter simplement parce que le fichier n'existe pas sur une machine spécifique. C'est particulièrement gênant dans une équipe : l'un a tout qui fonctionne, l'autre « plante au démarrage », et l'enquête commence.
Erreur n°3 : essayer de définir spring.config.location à l'intérieur de application.yml.
C'est une boucle logique, pas un paramètre. Les paramètres spring.config.location et spring.config.additional-location sont lus très tôt — avant même que Boot n'ait traité vos fichiers de configuration. C'est pourquoi ils sont définis via des variables d'environnement, -D ou des arguments de lancement. Si vous placez cela dans un fichier qui doit être trouvé via ces mêmes règles, vous obtenez « je cherche la carte pour trouver la carte ».
Erreur n°4 : confondre emplacement fixe et import relatif, puis s'étonner que « le fichier n'est pas trouvé ».
Si vous avez écrit spring.config.import: optional:extra/runtime.yml, vous avez délibérément choisi un chemin relatif. Il dépend de l'endroit où se trouve le fichier qui a déclaré l'import. Si vous déplacez le fichier ou modifiez la structure des répertoires, le chemin relatif change aussi. Pour un conteneur, l'emplacement fixe (file:/config/runtime.yml) est souvent plus simple pour ne pas se demander « par rapport à quoi ».
Erreur n°5 : importer trop de fichiers et construire un labyrinthe de configuration.
spring.config.import est un outil pratique, mais il ne doit pas se transformer en roman « Guerre et Paix » en 12 chapitres. Quand il y a trop d'imports, le débutant perd l'objectif principal du jour : la prévisibilité. Dans le cadre du cours, il est préférable de garder une chaîne courte : un seul fichier runtime externe ou un seul répertoire supplémentaire — et c'est tout.
GO TO FULL VERSION