CodeGym /Cours /Spring REST & MVC /Profils et redéfinitions de configuration

Profils et redéfinitions de configuration

Spring REST & MVC
Niveau 28, Leçon 3
Disponible

1. Profils Spring Boot

Quand un projet est petit, il semble que la configuration soit quelque chose comme « une ligne que l’on peut corriger et oublier ». Mais dès que vous implémentez des fonctionnalités réelles, par exemple des pièces jointes avec un stockage local, il s’avère soudain que le même code doit pouvoir s’exécuter de différentes manières : quelqu’un veut stocker les fichiers dans ./data, quelqu’un dans ./tmp, et sur le CI, on ne peut pas écrire n’importe où. Et là commence le drame classique « pourquoi ça marche chez moi et pas chez toi ».

Imaginez que vous ayez écrit LocalAttachmentStorage, qui enregistre les fichiers sur le disque. En local, vous voulez que les fichiers ne polluent pas le dépôt, vous les mettez donc dans ./tmp/attachments. Sur un autre environnement, vous voulez les stocker dans ./data/attachments parce que ce répertoire est sauvegardé ou simplement plus pratique. L’API, quant à elle, ne change pas : mêmes points d’extrémité, mêmes DTO, mêmes statuts. Seule « l’emplacement des fichiers » et « les limites de téléchargement » changent.

Si vous résolvez le problème « à l’arrache », vous arriverez rapidement à de mauvaises solutions : modifier application.yml à la main avant chaque lancement, avoir trois fichiers différents et copier de l’un à l’autre, commiter des chemins dépendants de la machine, ou pire, revenir au code en dur – « pour que ça marche à coup sûr ». Les profils dans Spring Boot ont été conçus justement pour que vous puissiez dire : « il y a des valeurs de base, et pour un mode d’exécution spécifique, j’ajoute un petit correctif qui modifie uniquement ce qui est nécessaire ».

Le profil comme « correctif » à application.yml

Le mot « profil » évoque souvent chez les débutants une image erronée : comme s’il s’agissait « d’un autre projet » ou « d’un mode de fonctionnement différent de l’application, où tout est autrement ». En pratique, un profil dans Spring Boot est bien plus simple et utile : c’est un moyen d’ajouter une couche supplémentaire par-dessus les paramètres de base pour en redéfinir certaines valeurs. Une métaphore commode est celle du « correctif » ou de l’« autocollant par-dessus l’étiquette de base ».

Le fichier de base application.yml est le costume par défaut de l’application : soigné, neutre, sans lien avec une machine particulière. Le profil local est votre « peignoir domestique » : il permet de déboguer confortablement, vous pouvez augmenter les limites, simplifier le répertoire, rendre les logs plus bavards. Mais c’est toujours la même personne, la même application, les mêmes points d’extrémité. Nous ne modifions pas le contrat de l’API avec le profil, nous ne changeons que « dans quelles chaussures courir » et « quelle veste mettre ».

Techniquement, Spring Boot doit résoudre deux problèmes : déterminer quel profil est actif et charger les paramètres supplémentaires relatifs à ce profil. Le profil peut être unique (local) ou multiple (local,debug), mais dans le projet d’apprentissage nous gardons cela aussi simple que possible : le application.yml de base définit la ligne de base du projet, application-local.yml est le correctif local, et les variables d’environnement et arguments de lancement restent pour les surcharges spécifiques à la machine.

2. application.yml de base et application-local.yml de profil

Pour que la surcharge locale ne se transforme pas en un monde de configuration séparé, nous gardons pour Task Tracker API une disposition canonique unique : le application.yml de base définit le comportement par défaut du projet, et application-local.yml ne modifie que ce qui est nécessaire en local. Ainsi, on voit immédiatement où se trouve la ligne de base du dépôt et où commence le confort d’un lancement particulier.

Voici à quoi pourrait ressembler votre application.yml de base, qui est commité dans le dépôt comme source unique de vérité pour le comportement par défaut :

# application.yml
spring:
  mvc:
    problemdetails:
      # Paramètre de base : activer ProblemDetail pour des erreurs uniformes
      enabled: true
  servlet:
    multipart:
      # Limites de base (communes à tous si elles ne sont pas redéfinies)
      max-file-size: 10MB
      max-request-size: 12MB

app:
  attachments:
    # Répertoire par défaut, convenable pour un lancement "général"
    storage-dir: ./data/attachments

Et voici le fichier de profil pour un lancement local :

# application-local.yml
spring:
  servlet:
    multipart:
      # Localement, on peut augmenter les limites pour ne pas gêner le développement
      max-file-size: 20MB
      max-request-size: 25MB

app:
  attachments:
    # Localement, on stocke dans tmp pour ne pas encombrer le répertoire de travail du projet
    storage-dir: ./tmp/attachments

Cela se lit simplement : « par défaut, on stocke les pièces jointes dans ./data/attachments, et en local dans ./tmp/attachments ; les limites de base sont 10MB/12MB, en local 20MB/25MB ». Ainsi, vous ne copiez pas tout le fichier de base dans le fichier de profil, vous ne redéfinissez que ce qui diffère vraiment.

En pratique, les équipes font souvent ainsi : application.yml est toujours commité, tandis que application-local.yml peut soit être commité, soit être personnel et se trouver dans .gitignore. Dans un projet d’apprentissage, il est souvent plus pratique de commiter un application-local.yml commun, et de définir les réglages tout à fait personnels via les variables d’environnement – nous y venons maintenant.

Le même correctif peut également être écrit à l’intérieur d’un unique application.yml en utilisant --- et spring.config.activate.on-profile. Spring Boot le prend en charge, mais pour le projet actuel, un fichier séparé application-local.yml se lit plus facilement : l’instantané de base et la surcharge locale ne sont pas mélangés dans le même fichier.

3. Lancement et redéfinitions

Une fois la disposition fixée, tout devient assez banal – et c’est un avantage ici. Nous activons simplement le profil local au lancement, et si une machine a besoin d’une surcharge privée supplémentaire, nous ajoutons une variable d’environnement par-dessus le profil.

Activation du profil au lancement

Les profils ne deviennent utiles que lorsque vous savez les gérer sans recourir à la magie. Bonne nouvelle : activer un profil dans Spring Boot peut se faire de plusieurs façons simples, toutes se ramenant à une clé unique : spring.profiles.active. Vous choisissez celle qui est la plus pratique pour votre mode de travail et ne sautez pas entre dix variantes « selon l’humeur ».

La méthode la plus « bouton » passe par la configuration de lancement dans l’IDE : dans IntelliJ IDEA, vous ajoutez une variable d’environnement SPRING_PROFILES_ACTIVE=local ou indiquez le profil actif dans le champ correspondant. C’est pratique car c’est visuel : ouvrez Run Configuration – vous voyez quel profil est activé.

Si vous lancez via Gradle, vous pouvez passer des arguments à bootRun. Dans un projet d’apprentissage, cela ressemble souvent à :

# Lancement de l'application via Gradle avec activation explicite du profil
./gradlew bootRun --args='--spring.profiles.active=local'

Si vous lancez un jar déjà compilé, la logique est la même, seul l’argument est transmis au processus JVM de l’application :

# Lancement du jar avec le profil actif local
java -jar build/libs/task-tracker.jar --spring.profiles.active=local

Un conseil pratique utile : lorsque vous configurez des profils pour la première fois, il est très pratique d’afficher les profils actifs dans le journal au démarrage de l’application, pour ne pas avoir à deviner « est-ce que ça s’est vraiment activé ? ». Dans un projet d’apprentissage, vous pouvez créer un petit composant-journaliseur. Il ne concerne pas la logique métier, donc sa place est dans la couche de configuration ou d’infrastructure.

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
public class ActiveProfilesLogger implements ApplicationRunner {
    private final Environment env;

    public ActiveProfilesLogger(Environment env) {
        // Environment est une source de configuration, on peut y lire les profils actifs
        this.env = env;
    }

    @Override
    public void run(ApplicationArguments args) {
        // Affiche les profils pour ne pas avoir à deviner ce qui est réellement activé au démarrage
        System.out.println("Active profiles: " + String.join(",", env.getActiveProfiles()));
        // Exemple de sortie attendue :
        // Active profiles: local
    }
}

Oui, System.out.println n’est pas l’approche « de production » la plus élégante, mais dans un projet d’apprentissage, cela permet de voir le résultat instantanément. Plus tard, vous utiliserez bien sûr un journaliseur normal, mais le principe reste le même : d’abord, assurez-vous que le profil est activé, ensuite seulement discutez avec le YAML.

Redéfinitions via les variables d’environnement

Les profils résolvent le problème « nous avons un mode local prévisible », mais ne résolvent pas le problème « chaque développeur a sa propre machine ». C’est là qu’interviennent les redéfinitions via les variables d’environnement. Leur principal avantage : vous pouvez modifier une valeur sans toucher au dépôt du tout. C’est le moyen idéal pour dire : « dans mon environnement, le stockage se trouve ailleurs, et c’est normal ».

Spring Boot sait récupérer les valeurs des variables d’environnement et les mapper sur les propriétés. Une règle utile à retenir dans la pratique : les points et les tirets se transforment en soulignés, le nom passe en majuscules. Ainsi, app.attachments.storage-dir devient APP_ATTACHMENTS_STORAGE_DIR, et spring.servlet.multipart.max-file-size devient SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE.

Voici un exemple de lancement typique où nous activons le profil local et redéfinissons simultanément le répertoire de stockage et la limite de fichier :

# 1) Activation du profil local
export SPRING_PROFILES_ACTIVE=local
# 2) Redéfinition d'une propriété spécifique à l'application (répertoire de stockage)
export APP_ATTACHMENTS_STORAGE_DIR=./runtime/attachments
# 3) Redéfinition d'une propriété du framework (limite de taille de fichier)
export SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE=30MB

Sous Windows dans PowerShell, la syntaxe est différente, mais l’idée est la même : une variable d’environnement est le « dernier mot » que vous dites à la configuration avant de lancer l’application.

Pourquoi est-ce pratique dans notre Task Tracker API ? Parce que les répertoires sont très personnels. Un développeur veut stocker les fichiers dans ./tmp, un autre dans C:\work\data, un troisième dans ~/dev/task-tracker-attachments. Si vous essayez de résoudre cela avec des profils et de commiter différents chemins, vous obtiendrez une « guerre de configuration ». Si vous le résolvez avec des variables d’environnement – tout le monde est content et le dépôt reste propre.

Priorités de configuration et débogage

Lorsque vous commencez à utiliser à la fois des profils, des variables d’environnement et des valeurs par défaut dans @ConfigurationProperties, une question légitime se pose : « si une même propriété est définie à trois endroits, laquelle est prise en compte ? ». Cette question n’est pas philosophique, mais tout à fait pratique : sans comprendre les priorités, vous passerez des heures à vous dire « j’ai bien modifié, pourquoi ça ne marche pas ».

Dans une version d’apprentissage, on peut garder un modèle mental simple : la configuration est constituée de couches, et plus on est « proche du lancement », plus la priorité est élevée. Le fichier de base application.yml fournit la valeur par défaut. Le fichier de profil remplace une partie des valeurs. Les variables d’environnement remplacent par-dessus le profil. Les paramètres de ligne de commande sont généralement encore plus prioritaires et peuvent même écraser les variables d’environnement.

Source de la valeur Exemple Priorité (qui « écrase » qui)
Valeur par défaut dans le code private String storageDir = "./data/attachments"; la plus basse
application.yml (base)
app.attachments.storage-dir: ./data/attachments
supérieure à la valeur par défaut
application-local.yml
./tmp/attachments
supérieure à base
Variables d’environnement
APP_ATTACHMENTS_STORAGE_DIR=...
supérieure aux fichiers
Arguments de ligne de commande
--app.attachments.storage-dir=...
généralement la plus haute

Si vous vous retrouvez dans une situation « j’ai modifié le YAML, mais la valeur ne change pas », la cause est presque toujours l’une des deux suivantes : soit vous n’exécutez pas le bon profil, soit la valeur est écrasée par une variable d’environnement/un argument. Par conséquent, lorsque quelque chose se comporte étrangement, ne commencez pas par le mysticisme. Commencez par un diagnostic : vérifiez le profil actif, puis vérifiez si vous n’avez pas une variable d’environnement qui redéfinit la même propriété.

Pour fixer les idées, on peut imaginer cela comme un organigramme très simple : Spring Boot parcourt du haut vers le bas et « assemble » la configuration, mais en cas de conflit, la source la plus prioritaire l’emporte.

flowchart TD
    %% Idée : ci-dessous se trouvent les sources les plus prioritaires ; elles "écrasent" les supérieures
    A["Valeurs par défaut dans le code"] --> B["application.yml"]
    B --> C["application-local.yml"]
    C --> D["Variables d’environnement"]
    D --> E["Arguments de ligne de commande"]
    %% Assemblage final des valeurs que votre code verra dans @ConfigurationProperties
    E --> F["Valeur finale dans @ConfigurationProperties"]

À la fin de cette journée, il est utile de pouvoir répondre sans aide à plusieurs questions : pouvez-vous expliquer pourquoi storage-dir est exactement telle valeur ; comprenez-vous comment activer le profil local lors d’un lancement spécifique ; pouvez-vous redéfinir un paramètre via une variable d’environnement sans toucher au YAML ; pouvez-vous prouver rapidement que le profil est réellement actif, et non « probablement actif ».

4. Erreurs typiques lors du travail avec les profils et les redéfinitions

Erreur n°1 : copier tout le fichier de configuration de base dans le profil.
Cela se produit généralement « par habitude » : le développeur crée application-local.yml, prend tout application.yml, le colle dedans et modifie une ligne. Une semaine plus tard, le fichier de base a changé, mais le profil non. On se retrouve avec deux mondes qui divergent, et le débogage devient un jeu de devinettes. Le profil ne doit contenir que les différences, sinon il cesse d’être un correctif et devient une « seconde réalité ».

Erreur n°2 : forcer le profil à modifier le sens du contrat de l’API.
On a très envie de faire « en local, le statut peut être n’importe lequel, mais en production non », ou « en local, désactivons la validation pour qu’elle ne gêne pas », ou « en local, on renvoie les erreurs sous forme de chaîne, et en production en ProblemDetail ». Cela détruit l’idée même de contrat : le client ne doit pas dépendre du profil activé par le serveur aujourd’hui. Le profil concerne l’environnement d’exécution, pas les règles du domaine, pas les transitions de statut autorisées et pas la forme des DTO. Si une valeur affecte le contrat, sa place est dans le code et la documentation, pas dans application-local.yml.

Erreur n°3 : oublier quel profil est actif, et le vérifier par la méthode « je vais m’énerver sur la vie ».
Situation typique : vous êtes sûr d’avoir activé local, mais en réalité l’application a démarré sans profil, donc storage-dir vient du fichier de base. Ou l’inverse : le profil est activé quelque part dans l’IDE, vous l’avez oublié, et ensuite vous vous étonnez que sur votre portable tout fonctionne « différemment du serveur ». On y remédie simplement : en développement, affichez toujours le profil actif, et dans l’IDE, gardez des configurations d’exécution séparées « Default » et « Local » pour ne pas basculer manuellement.

Erreur n°4 : mettre un chemin absolu dépendant de la machine dans le application.yml de base.
Le fichier de configuration de base est ce que tout le monde doit lire de la même manière. Si vous y écrivez /Users/alex/... ou D:\attachments, vous transformez le dépôt en « configuration pour une seule machine ». Pour le fichier de base, mieux vaut un chemin relatif comme ./data/attachments. Si quelqu’un a besoin d’un chemin absolu, qu’il le définisse via une variable d’environnement ou via un profil local qui n’est pas commité.

Erreur n°5 : ne pas comprendre que les variables d’environnement peuvent « écraser » le YAML, et donc ne pas en croire ses yeux.
Le scénario est comique : vous modifiez application.yml, redémarrez, mais la valeur ne change pas. Vous commencez à soupçonner une conspiration de Spring Boot. En réalité, le plus souvent, vous avez déjà défini APP_ATTACHMENTS_STORAGE_DIR quelque part, et cette valeur a gagné. À ce moment, il est utile de ne pas s’énerver contre Spring, mais de regarder calmement les variables d’environnement et de se souvenir de l’ordre des priorités. Spring Boot se comporte ici de façon très cohérente – c’est nous qui avons juste oublié que nous avons laissé une surcharge.

1
Mission
Spring REST & MVC, niveau 28, leçon 3
Bloqué
Profil `local` pour un répertoire de stockage distinct
Profil `local` pour un répertoire de stockage distinct
1
Mission
Spring REST & MVC, niveau 28, leçon 3
Bloqué
Redéfinition via variable d'environnement par-dessus un profil
Redéfinition via variable d'environnement par-dessus un profil
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION