Aller au contenu principal

TP5.3 - Configuration par Profils

Objectifs​

À la fin de ce TP, vous serez capable de:

  1. ✅ Créer des profils pour dev, test, et prod
  2. ✅ Créer des Beans conditionnels selon le profil
  3. ✅ Utiliser des fichiers application-\{profile\}.properties
  4. ✅ Activer un profil et tester

Durée estimée: 1h30


Prérequis​

  • ✅ Chapitre 4 du Module 5 complété
  • ✅ TP5.1 et TP5.2 complétés
  • ✅ Maven 3.9+ et Java 17+

Préparation: Nouveau Projet​

Créer le Projet​

Via Spring Initializr:

  • Project: Maven
  • Spring Boot: 3.2.0
  • Artifact: profiles-demo
  • Dependencies: Spring Web, Spring Data JPA, H2 Database, PostgreSQL Driver

Structure:

src/main/java/com/example/profiles/
├── ProfilesDemoApplication.java
├── service/
│ ├── EmailService.java
│ ├── FakeEmailService.java
│ └── SmtpEmailService.java
├── config/
│ ├── DevConfig.java
│ ├── ProdConfig.java
│ └── DatabaseConfig.java
└── controller/
└── TestController.java

src/main/resources/
├── application.properties
├── application-dev.properties
├── application-test.properties
└── application-prod.properties

Exercice 1: Profils pour Services Email​

Objectif​

Créer 2 implémentations d'EmailService: une fake pour dev, une réelle pour prod.

Étape 1.1: Créer l'Interface​

Fichier: service/EmailService.java

package com.example.profiles.service;

public interface EmailService {
void send(String to, String subject, String body);
}

Étape 1.2: Implémentation DEV (Fake)​

Fichier: service/FakeEmailService.java

package com.example.profiles.service;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile("dev") // ← Actif SEULEMENT en dev
public class FakeEmailService implements EmailService {

@Override
public void send(String to, String subject, String body) {
System.out.println("========== FAKE EMAIL ==========");
System.out.println("To: " + to);
System.out.println("Subject: " + subject);
System.out.println("Body: " + body);
System.out.println("================================");
System.out.println("(Email simulé - pas envoyé réellement)");
}
}

Points clés:

  • @Profile("dev") → Bean créé seulement en profil dev
  • Email simulé → Pas de vrai envoi

Étape 1.3: Implémentation PROD (Réelle)​

Fichier: service/SmtpEmailService.java

package com.example.profiles.service;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile("prod") // ← Actif SEULEMENT en prod
public class SmtpEmailService implements EmailService {

@Override
public void send(String to, String subject, String body) {
System.out.println("========== SMTP EMAIL ==========");
System.out.println("Connexion au serveur SMTP...");
System.out.println("Authentification...");
System.out.println("Envoi à: " + to);
System.out.println("Sujet: " + subject);
System.out.println("================================");
System.out.println("(Email réellement envoyé)");

// En production, ici vous utiliseriez JavaMail:
// Session session = Session.getInstance(properties, authenticator);
// MimeMessage message = new MimeMessage(session);
// Transport.send(message);
}
}

Étape 1.4: Créer le Controller de Test​

Fichier: controller/TestController.java

package com.example.profiles.controller;

import com.example.profiles.service.EmailService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/test")
public class TestController {

private final EmailService emailService;

@Value("${spring.profiles.active:default}")
private String activeProfile;

public TestController(EmailService emailService) {
this.emailService = emailService;
}

@GetMapping("/email")
public String testEmail() {
emailService.send(
"user@example.com",
"Test Email",
"Ceci est un email de test"
);

return "✅ Email envoyé via profil: " + activeProfile +
" - Voir console";
}

@GetMapping("/profile")
public String getActiveProfile() {
return "Profil actif: " + activeProfile;
}
}

Étape 1.5: Activer le Profil DEV​

Fichier: src/main/resources/application.properties

# Profil actif
spring.profiles.active=dev

# Nom de l'application
spring.application.name=Profiles Demo

Étape 1.6: Tester en DEV​

Démarrez:

mvn clean install -DskipTests spring-boot:run

Console au démarrage:

The following 1 profile is active: "dev"

Testez:

http://localhost:8080/api/test/email

Console:

========== FAKE EMAIL ==========
To: user@example.com
Subject: Test Email
Body: Ceci est un email de test
================================
(Email simulé - pas envoyé réellement)

✅ En DEV: Email simulé!


Étape 1.7: Tester en PROD​

Modifiez application.properties:

spring.profiles.active=prod

Redémarrez et testez:

Console:

The following 1 profile is active: "prod"

========== SMTP EMAIL ==========
Connexion au serveur SMTP...
Authentification...
Envoi à: user@example.com
Sujet: Test Email
================================
(Email réellement envoyé)

✅ En PROD: Email réel!


Exercice 2: Configuration Base de Données par Profil​

Objectif​

Utiliser H2 en dev/test, PostgreSQL en prod.

Étape 2.1: Fichier application.properties (Commun)​

Fichier: src/main/resources/application.properties

# Profil actif (changez selon besoin)
spring.profiles.active=dev

# Configuration commune
spring.application.name=Profiles Demo
server.port=8080

# JPA commun
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Étape 2.2: Configuration DEV (H2)​

Fichier: src/main/resources/application-dev.properties

# Base de données H2 en mémoire
spring.datasource.url=jdbc:h2:mem:devdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# Console H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

# Hibernate
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Logs
logging.level.root=DEBUG
logging.level.com.example.profiles=DEBUG

Étape 2.3: Configuration TEST (H2 avec scripts)​

Fichier: src/main/resources/application-test.properties

# Base de données H2 en mémoire pour tests
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# Hibernate
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# Logs minimaux pour tests
logging.level.root=WARN
logging.level.com.example.profiles=INFO

Étape 2.4: Configuration PROD (PostgreSQL)​

Fichier: src/main/resources/application-prod.properties

# Base de données PostgreSQL
spring.datasource.url=jdbc:postgresql://localhost:5432/proddb
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=admin
spring.datasource.password=admin123

# Hibernate
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

# Logs minimaux
logging.level.root=WARN
logging.level.com.example.profiles=INFO

# Sécurité (en production, utilisez des variables d'environnement!)
# spring.datasource.username=${DB_USERNAME}
# spring.datasource.password=${DB_PASSWORD}

Étape 2.5: Créer une Entité de Test​

Fichier: model/User.java

package com.example.profiles.model;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;

// Constructeurs
public User() {}

public User(String name, String email) {
this.name = name;
this.email = email;
}

// Getters et Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}

Étape 2.6: Tester les Différentes BDs​

En DEV (H2):

spring.profiles.active=dev

Démarrez et vérifiez la console:

H2 console available at '/h2-console'
Hibernate: create table users (...)
Database: H2

Accédez à la console H2:

http://localhost:8080/h2-console
JDBC URL: jdbc:h2:mem:devdb
Username: sa
Password: (vide)

En PROD (PostgreSQL):

  1. Créez d'abord la base:
CREATE DATABASE proddb;
  1. Modifiez le profil:
spring.profiles.active=prod
  1. Démarrez:
Database: PostgreSQL
Hibernate: validate schema

Exercice 3: Beans de Configuration par Profil​

Objectif​

Créer des Beans différents selon l'environnement.

Étape 3.1: Configuration DEV​

Fichier: config/DevConfig.java

package com.example.profiles.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("dev") // ← Toute la classe active en dev
public class DevConfig {

@Bean
public String environmentName() {
return "DEVELOPMENT";
}

@Bean
public boolean debugMode() {
return true;
}

@Bean
public String apiUrl() {
return "http://localhost:8080/api";
}
}

Étape 3.2: Configuration PROD​

Fichier: config/ProdConfig.java

package com.example.profiles.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("prod") // ← Toute la classe active en prod
public class ProdConfig {

@Bean
public String environmentName() {
return "PRODUCTION";
}

@Bean
public boolean debugMode() {
return false;
}

@Bean
public String apiUrl() {
return "https://api.monsite.com/api";
}
}

Étape 3.3: Tester​

Ajoutez dans TestController.java:

@RestController
@RequestMapping("/api/test")
public class TestController {

private final EmailService emailService;
private final String environmentName;
private final boolean debugMode;
private final String apiUrl;

@Value("${spring.profiles.active:default}")
private String activeProfile;

public TestController(
EmailService emailService,
String environmentName,
boolean debugMode,
String apiUrl
) {
this.emailService = emailService;
this.environmentName = environmentName;
this.debugMode = debugMode;
this.apiUrl = apiUrl;
}

// ... autres méthodes ...

@GetMapping("/config")
public String getConfig() {
return String.format(
"Environnement: %s<br>" +
"Profil actif: %s<br>" +
"Mode debug: %s<br>" +
"API URL: %s",
environmentName,
activeProfile,
debugMode,
apiUrl
);
}
}

En DEV:

Environnement: DEVELOPMENT
Profil actif: dev
Mode debug: true
API URL: http://localhost:8080/api

En PROD:

Environnement: PRODUCTION
Profil actif: prod
Mode debug: false
API URL: https://api.monsite.com/api

Exercice 4: Profils Multiples et @Profile Composé​

Objectif​

Activer plusieurs profils et créer des conditions complexes.

Étape 4.1: Bean pour Plusieurs Profils​

Ajoutez dans config/DevConfig.java:

@Bean
@Profile({"dev", "test"}) // ← Actif en dev OU test
public String sharedDevTestBean() {
return "Bean partagé entre dev et test";
}

Étape 4.2: Bean Conditionnel Complexe​

@Bean
@Profile("!prod") // ← Actif dans TOUS les profils SAUF prod
public String nonProdBean() {
return "Bean pour non-production (dev, test, etc.)";
}

Étape 4.3: Activer Plusieurs Profils​

Méthode 1: application.properties

spring.profiles.active=dev,mock-data,debug

Méthode 2: Ligne de commande

mvn spring-boot:run -Dspring-boot.run.profiles=dev,mock-data

Méthode 3: Variable d'environnement

export SPRING_PROFILES_ACTIVE=prod,monitoring
mvn spring-boot:run

Exercice 5: Configuration Complète Multi-Environnements​

Objectif​

Créer une configuration production-ready.

Étape 5.1: Variables d'Environnement en PROD​

Fichier: application-prod.properties

# Base de données (depuis variables d'environnement)
spring.datasource.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/proddb}
spring.datasource.username=${DB_USERNAME:admin}
spring.datasource.password=${DB_PASSWORD}

# SMTP (depuis variables d'environnement)
spring.mail.host=${SMTP_HOST:smtp.gmail.com}
spring.mail.port=${SMTP_PORT:587}
spring.mail.username=${SMTP_USERNAME}
spring.mail.password=${SMTP_PASSWORD}

# API Keys
app.external-api.key=${EXTERNAL_API_KEY}
app.external-api.url=${EXTERNAL_API_URL:https://api.service.com}

Syntaxe:

  • $\{VAR_NAME\} → Variable obligatoire (erreur si absente)
  • ${VAR_NAME:default} → Variable avec valeur par défaut

Étape 5.2: Déployer avec Variables​

Linux/Mac:

export DATABASE_URL="jdbc:postgresql://prod-server:5432/myapp"
export DB_USERNAME="app_user"
export DB_PASSWORD="secret123"
export SMTP_USERNAME="noreply@myapp.com"
export SMTP_PASSWORD="smtp_secret"

mvn spring-boot:run

Windows:

$env:DATABASE_URL="jdbc:postgresql://prod-server:5432/myapp"
$env:DB_USERNAME="app_user"
# ...

mvn spring-boot:run

Récapitulatif​

Structure des Fichiers​

application.properties          ← Configuration commune
application-dev.properties ← Spécifique dev
application-test.properties ← Spécifique test
application-prod.properties ← Spécifique prod

Ordre de priorité:

1. application-{profile}.properties (spécifique)
2. application.properties (commune)
3. Variables d'environnement
4. Arguments ligne de commande

Beans par Profil​

// Bean pour UN profil
@Profile("dev")

// Bean pour PLUSIEURS profils
@Profile({"dev", "test"})

// Bean pour TOUS sauf un
@Profile("!prod")

// Classe entière
@Configuration
@Profile("dev")
public class DevConfig { }

Activer un Profil​

1. application.properties:

spring.profiles.active=dev

2. Ligne de commande:

--spring.profiles.active=prod

3. Variable d'environnement:

export SPRING_PROFILES_ACTIVE=prod

4. IDE (VS Code):

"spring.profiles.active": "dev"

Auto-Évaluation​

  1. Quelle est la différence entre application.properties et application-dev.properties?
  2. Comment créer un Bean actif seulement en dev?
  3. Comment activer plusieurs profils simultanément?
  4. Pourquoi utiliser des variables d'environnement en production?
  5. Quelle est la syntaxe pour une variable avec valeur par défaut?

Challenge Bonus 🎯​

Créez une configuration complète pour une vraie application:

  1. 3 profils: dev, test, prod

  2. Dev:

    • H2 en mémoire
    • Email fake
    • Logs DEBUG
    • Pas de sécurité
  3. Test:

    • H2 avec scripts
    • Email mock
    • Logs INFO
  4. Prod:

    • PostgreSQL
    • Email SMTP réel
    • Logs WARN
    • Sécurité activée
    • Variables d'environnement

Bonus: Ajoutez un profil docker pour Docker Compose


Félicitations! 🎉​

Vous avez terminé le Module 5!

Vous maîtrisez maintenant:

  • ✅ Inversion de Contrôle (IoC)
  • ✅ Injection de Dépendances (DI)
  • ✅ Beans et leur cycle de vie
  • ✅ Scopes (Singleton, Prototype)
  • ✅ Configuration par profils
  • ✅ Multi-environnements (dev/test/prod)

Vous êtes prêt pour des concepts avancés!


⬅️ TP précédent: Beans et Scopes

⬅️ Retour au Module 5

➡️ Module suivant: Spring Data JPA Avancé