Aller au contenu principal

Chapitre 1: Inversion de Contrôle (IoC)

Introduction​

Bienvenue! Dans ce chapitre, vous allez comprendre le principe fondamental qui fait fonctionner Spring.

Ce que vous allez apprendre​

  1. Le problème du couplage fort (et pourquoi c'est mauvais)
  2. La solution: l'Inversion de Contrôle
  3. Comment Spring implémente IoC
  4. Le Container Spring et son rôle

Durée: 30 minutes


Le Problème: Le Couplage Fort​

Situation Réelle​

Imaginez que vous développez une application e-commerce. Vous avez besoin d'envoyer des emails aux clients.

❌ Approche Traditionnelle (Sans IoC)​

public class OrderService {

// On crée directement l'objet EmailService
private EmailService emailService = new EmailService();

public void processOrder(Order order) {
// Logique métier
saveOrder(order);

// Envoyer email de confirmation
emailService.sendEmail(order.getCustomerEmail(), "Commande confirmée");
}
}

Pourquoi C'est Problématique?​

Problème 1: Couplage Fort​

private EmailService emailService = new EmailService();
// ^^^^^^^^^^^^^^^^
// Dépendance DIRECTE!

Conséquence:

  • OrderService DÉPEND DIRECTEMENT de EmailService
  • Impossible de changer l'implémentation facilement
  • Difficile à tester

Problème 2: Impossible de Changer l'Implémentation​

Scénario: Vous voulez passer de Gmail à SendGrid pour envoyer les emails.

// Vous devez MODIFIER OrderService
public class OrderService {
// Avant
private EmailService emailService = new EmailService();

// Après - IL FAUT CHANGER LE CODE!
private SendGridEmailService emailService = new SendGridEmailService();
// ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
}

Problème: Modifier du code qui fonctionne = risque de bugs!


Problème 3: Impossible de Tester​

Vous voulez tester OrderService sans envoyer de vrais emails:

@Test
public void testProcessOrder() {
OrderService service = new OrderService();
Order order = new Order(...);

service.processOrder(order);

// PROBLÈME: Un vrai email sera envoyé!
// Impossible de "mocker" EmailService
}

Résumé du Problème​

OrderService ──────────> EmailService
(dépendance directe)

❌ Couplage fort
❌ Difficile à modifier
❌ Difficile à tester
❌ Code rigide et fragile

La Solution: Inversion de Contrôle (IoC)​

Principe de Base​

"Ne créez pas vos dépendances, demandez-les!"

Au lieu de:

private EmailService emailService = new EmailService();  // ❌ JE crée

Faites:

private EmailService emailService;  // ✅ QUELQU'UN me le donne

Qu'est-ce que l'IoC?​

Inversion de Contrôle (IoC) = Inverser qui contrôle la création des objets

Avant IoC (Contrôle Normal)​

OrderService dit: "J'ai besoin d'un EmailService"
OrderService dit: "Je vais le créer moi-même avec 'new'"

Avec IoC (Contrôle Inversé)​

OrderService dit: "J'ai besoin d'un EmailService"
Spring dit: "Pas de problème, je te le donne!"
Spring crée EmailService et le donne à OrderService

Le contrôle de la création est INVERSÉ: c'est Spring qui gère, pas vous!


✅ Approche avec IoC (Injection de Dépendances)​

Code Amélioré​

@Service
public class OrderService {

// On ne crée PAS l'objet
private final EmailService emailService;

// On DEMANDE l'objet via le constructeur
public OrderService(EmailService emailService) {
this.emailService = emailService;
}

public void processOrder(Order order) {
saveOrder(order);
emailService.sendEmail(order.getCustomerEmail(), "Commande confirmée");
}
}

Ce Qui Se Passe​


Avantages de l'IoC​

1. Couplage Faible​

// OrderService ne connaît PAS l'implémentation concrète
private final EmailService emailService;
// ^^^^^^^^^^^^
// Interface (abstraction)

Vous pouvez avoir plusieurs implémentations:

public interface EmailService {
void sendEmail(String to, String message);
}

public class GmailEmailService implements EmailService {
// Envoie via Gmail
}

public class SendGridEmailService implements EmailService {
// Envoie via SendGrid
}

Spring choisit laquelle utiliser selon la configuration!


2. Facile à Modifier​

Changer d'implémentation SANS toucher OrderService:

// Configuration Spring
@Configuration
public class AppConfig {

@Bean
public EmailService emailService() {
// Avant
// return new GmailEmailService();

// Après - Changement en UN seul endroit!
return new SendGridEmailService();
}
}

OrderService n'a PAS besoin de changer!


3. Facile à Tester​

@Test
public void testProcessOrder() {
// On crée un "faux" EmailService pour les tests
EmailService fakeEmailService = new EmailService() {
@Override
public void sendEmail(String to, String message) {
System.out.println("Email simulé envoyé à: " + to);
// Pas de vrai email envoyé!
}
};

// On injecte le faux service
OrderService service = new OrderService(fakeEmailService);

Order order = new Order(...);
service.processOrder(order);

// ✅ Pas de vrai email envoyé!
}

Le Container Spring (ApplicationContext)​

Qu'est-ce que le Container?​

Le Container Spring = Le gestionnaire d'objets de Spring

Ses responsabilités:

  1. ✅ Créer les objets (beans)
  2. ✅ Les configurer
  3. ✅ Les assembler (injecter les dépendances)
  4. ✅ Gérer leur cycle de vie

Analogie​

Pensez au Container Spring comme un chef d'orchestre:

Chef d'Orchestre (Container Spring)
↓
Musiciens (Beans: Controller, Service, Repository)
↓
L'orchestre joue de la musique (L'application fonctionne)

Le chef d'orchestre:

  • ✅ Recrute les musiciens (crée les beans)
  • ✅ Les place au bon endroit (injection)
  • ✅ Coordonne leur travail (gestion du cycle de vie)

Le Container en Action​

// Au démarrage de l'application
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// Cette ligne démarre le Container Spring
SpringApplication.run(DemoApplication.class, args);
// ^^^
// Le Container démarre ici!
}
}

Ce que fait le Container au démarrage:

1. Scanner les classes avec @Component, @Service, @Repository, @Controller
↓
2. Créer les instances (beans)
↓
3. Résoudre les dépendances
↓
4. Injecter les dépendances
↓
5. Application prête!

Visualisation du Container​

Le Container gère TOUS les objets et leurs dépendances!


Exemple Complet​

Sans IoC (Ancien Code)​

public class OrderService {
private EmailService emailService = new EmailService();
private PaymentService paymentService = new PaymentService();
private InventoryService inventoryService = new InventoryService();

public void processOrder(Order order) {
paymentService.charge(order);
inventoryService.reserve(order);
emailService.sendConfirmation(order);
}
}

Problèmes:

  • ❌ 3 dépendances créées avec new
  • ❌ Couplage fort partout
  • ❌ Impossible à tester
  • ❌ Rigide et fragile

Avec IoC (Code Spring)​

@Service
public class OrderService {

// Déclaration des dépendances (pas de 'new')
private final EmailService emailService;
private final PaymentService paymentService;
private final InventoryService inventoryService;

// Injection via le constructeur
public OrderService(
EmailService emailService,
PaymentService paymentService,
InventoryService inventoryService
) {
this.emailService = emailService;
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}

public void processOrder(Order order) {
paymentService.charge(order);
inventoryService.reserve(order);
emailService.sendConfirmation(order);
}
}

Avantages:

  • ✅ 0 utilisation de new (Spring gère tout)
  • ✅ Couplage faible
  • ✅ Facile à tester (on peut injecter des fakes)
  • ✅ Flexible et maintenable

Concepts Clés à Retenir​

1. Inversion de Contrôle (IoC)​

"Spring contrôle la création des objets, pas vous."

Avant: Vous créez vos objets avec new
Après: Spring crée vos objets et vous les donne


2. Injection de Dépendances (DI)​

"Spring injecte (donne) les dépendances dont vous avez besoin."

Mécanisme: Via le constructeur, setters, ou directement dans les champs


3. Container Spring​

"Le gestionnaire central qui crée et assemble tous les objets."

Responsabilités:

  • Créer les beans
  • Résoudre les dépendances
  • Injecter les dépendances
  • Gérer le cycle de vie

Résumé du Chapitre​

Le Problème​

// ❌ Couplage fort
private EmailService service = new EmailService();

Conséquences:

  • Difficile à modifier
  • Difficile à tester
  • Code rigide

La Solution​

// ✅ IoC + Injection de Dépendances
@Service
public class OrderService {
private final EmailService service;

public OrderService(EmailService service) {
this.service = service;
}
}

Avantages:

  • Couplage faible
  • Facile à modifier
  • Facile à tester
  • Code flexible

Analogie Finale​

IoC = Restaurant

Sans IoC:

Vous: "J'ai faim"
Vous: "Je vais acheter les ingrédients"
Vous: "Je vais cuisiner"
Vous: "Je mange"

Avec IoC:

Vous: "J'ai faim" (déclaration du besoin)
Restaurant: "Voici votre plat!" (Spring fournit)
Vous: "Je mange" (vous utilisez)

Vous ne créez pas le plat, on vous le DONNE. C'est ça l'IoC!


Auto-Évaluation​

Répondez à ces questions pour vérifier votre compréhension:

  1. Qu'est-ce que l'Inversion de Contrôle?
  2. Quel est le problème du couplage fort?
  3. Quel est le rôle du Container Spring?
  4. Pourquoi IoC facilite les tests?

Si vous ne pouvez pas répondre, relisez le chapitre!


Prochaine Étape​

Maintenant que vous comprenez POURQUOI l'IoC existe, passons au COMMENT: l'Injection de Dépendances.

➡️ Chapitre 2: Injection de Dépendances

⬅️ Retour au Module 5