---
title: "CDI : Inversion de contrôle et injection de dépendances"
description: "Introduction conceptuelle à CDI : le pattern d'inversion de contrôle, les beans gérés, les scopes, l'injection de dépendances, les qualifiers et les événements."
categories:
  - Java
  - I311
  - Lecture
  - IoC
  - CDI
provide_notes: true
provide_slides: true
---

## Objectifs

À la fin de cette leçon, vous serez capable de :

- **Comprendre l'inversion de contrôle (IoC)** et le rôle de CDI dans l'écosystème Jakarta EE.
- **Identifier un bean géré** par le conteneur CDI, ainsi que les règles qui le définissent.
- **Choisir le scope approprié** à un bean selon son cycle de vie.
- **Injecter des dépendances** entre beans, en privilégiant l'injection par constructeur.
- **Créer et consommer des qualifiers** pour distinguer des implémentations d'un même type.
- **Émettre et observer des événements** pour un découplage des composants.

> **Comment suivre cette leçon** : ce document est le **cœur** conceptuel. Il s'accompagne
> d'un TP pratique (`02_java_CDI_P_cdi-injection.qmd`) et d'une partie avancée optionnelle
> (`02_java_CDI_L_advanced.qmd`) pour les approfondissements.

## Introduction à CDI

**CDI** (*Contexts and Dependency Injection*) repose sur le **design pattern d'inversion de contrôle (IoC)** : c'est le conteneur, et non le code applicatif, qui crée, assemble et gère le cycle de vie des objets.

La spécification **CDI**, au cœur de *Jakarta EE*, fournit un cadre puissant pour la gestion déclarative des dépendances et des cycles de vie des objets au sein des applications Java.

### Objectifs de CDI

- **Injection de Dépendances** : éliminer le code de configuration explicite en injectant automatiquement les dépendances entre composants.
- **Gestion des Contextes** : contrôler finement le cycle de vie des objets selon leur portée (application, session, requête, etc.).
- **Programmation Orientée Aspect** : ajouter des fonctionnalités transversales (logging, sécurité, transactions) via intercepteurs et décorateurs.
- **Communication Découplée** : permettre aux composants de communiquer via des événements, sans couplage direct.

> **Principe Fondamental** : couplage faible avec un typage fort.

Un exemple de code simple utilisant CDI : [sample-cdi](https://github.com/ebpro/sample-cdi)

## Problèmes Résolus par CDI

CDI résout des problèmes récurrents dans le développement d'applications Java :

:::: {.columns}
::: {.column width="50%"}

**Sans CDI**

- **Accès aux objets** : références manuelles, couplage fort entre composants
- **Gestion du cycle de vie** : création/destruction manuelle des objets
- **Multi-threading** : gestion complexe de la concurrence
- **Mémoire** : risque de fuites mémoire par gestion manuelle des références
- **Implémentations alternatives** : configuration manuelle et rigide au déploiement
- **Partage d'objets** : gestion manuelle des références partagées

:::
::: {.column width="50%"}

**Avec CDI**

- **Accès aux objets** : injection automatique avec `@Inject`
- **Gestion du cycle de vie** : scopes automatiques (`@RequestScoped`, `@ApplicationScoped`, etc.)
- **Multi-threading** : gestion automatique par les contextes
- **Mémoire** : destruction automatique selon le scope
- **Implémentations alternatives** : qualifiers (`@Qualifier`) et alternatives (`@Alternative`)
- **Partage d'objets** : les scopes facilitent le partage sécurisé

:::
::::

### Avantages Clés

- ✅ **Faible couplage** : composants indépendants et interchangeables
- ✅ **Testabilité** : injection de mocks/stubs simplifiée
- ✅ **Extensibilité** : ajout de fonctionnalités sans modifier le code source
- ✅ **Maintenabilité** : code plus clair et mieux organisé

## Historique de CDI

CDI naît en **2009** avec la version **1.0** (Java EE 6, JSR 299), qui standardise l'injection de dépendances en s'inspirant de Spring. La version **2.0** (2017, Java EE 8) introduit les événements asynchrones (`@ObservesAsync`) et le support Java SE. La version **4.0** (2022, Jakarta EE 10) apporte **CDI Lite**, un sous-ensemble optimisé pour la compilation native, ainsi que des extensions compatibles au build.

## Qu'est-ce qu'un Bean CDI ?

Un **bean CDI** est un composant géré par le conteneur CDI qui respecte certaines règles et conventions.

### Vue Conceptuelle

Un bean CDI est défini par un **contrat d'injection** comprenant :

1. **Un ensemble de types** — les classes et interfaces implémentées/étendues (ex. `class PayPalService implements PaymentService`), qui permettent l'injection par type.
2. **Un ensemble de qualifiers** — des annotations qui distinguent les implémentations (ex. `@PayPal`, `@CreditCard`), permettant une injection spécifique.
3. **Un scope (contexte)** — définit le cycle de vie du bean (ex. `@ApplicationScoped`, `@RequestScoped`) et gère automatiquement sa création/destruction.
4. **Une implémentation** — le code métier concret du bean.
5. **Optionnellement : un nom** — pour la référence dans Expression Language (EL) (ex. `@Named("paypal")` → `#{paypal}` dans JSF).
6. **Optionnellement : des intercepteurs** — des comportements transversaux (logging, sécurité).

### Vue Technique

CDI définit techniquement un bean comme :

:::: {.columns}
::: {.column width="50%"}

**✅ Bean valide si :**

- Classe avec un constructeur sans paramètre :

```java
public class MyService {
    public MyService() {}
}
```

- Ou classe avec un constructeur annoté `@Inject` :

```java
public class MyService {
    @Inject
    public MyService(Logger logger) {}
}
```

:::
::: {.column width="50%"}

**❌ Pas un bean si :**

- Classe abstraite (sans `@Produces`)
- Interface (sans `@Produces`)
- Classe sans constructeur approprié
- Inner class non statique
- Classe avec plusieurs constructeurs non annotés

:::
::::

### Résumé : contrat d'un bean CDI

```text
Bean CDI = Types + Qualifiers + Scope + Implémentation
           [+ Nom EL] [+ Intercepteurs] [+ Alternative]
```

## Scopes et Cycle de Vie

Les **scopes** définissent la durée de vie et la visibilité des beans CDI.

### Scopes Principaux

| Scope | Durée de vie | Cas d'usage | Exemple |
|-------|--------------|-------------|---------|
| **`@Dependent`** | Même durée que le bean consommateur | Bean utilitaire, pas de partage | Calculateur, validateur |
| **`@RequestScoped`** | Une requête HTTP | Données formulaire, requête REST | Formulaire JSF, DTO REST |
| **`@SessionScoped`** | Session utilisateur | Données utilisateur persistantes | Panier e-commerce, préférences |
| **`@ApplicationScoped`** | Application entière | Configuration, services partagés | Cache, configuration globale |
| **`@ConversationScoped`** | Conversation utilisateur multi-requêtes | Processus métier multi-étapes | Tunnel de commande, wizard |

### Scope par Défaut

- Si aucun scope n'est spécifié, le bean est `@Dependent`.
- Un bean `@Dependent` n'est jamais partagé : chaque injection crée une nouvelle instance.

### Exemple de Scopes

```java
// Service partagé dans toute l'application
@ApplicationScoped
public class ConfigurationService {
    private Properties config;

    @PostConstruct
    public void init() {
        config = loadConfiguration();
    }
}
```

```java
// Bean lié à une requête HTTP
@RequestScoped
public class OrderFormBean {
    private String productId;
    private int quantity;
    // getters/setters
}
```

### Passivation et Activation

La **passivation**/activation (`@PrePassivate`/`@PostActivate`) des beans `@SessionScoped`/`@ConversationScoped` est abordée en partie avancée.

## Injection de Dépendances

L'injection de dépendances est le mécanisme central de CDI : le conteneur fournit automatiquement les dépendances d'un bean.

### Injection par Constructeur (recommandée)

```java
@ApplicationScoped
public class OrderService {
    private final PaymentService paymentService;
    private final Logger logger;

    @Inject
    public OrderService(PaymentService paymentService, Logger logger) {
        this.paymentService = paymentService;
        this.logger = logger;
    }
}
```

- ✅ Dépendances finales (immutabilité)
- ✅ Facilite les tests (injection manuelle)
- ✅ Dépendances explicites
- ✅ **Recommandée**

### Injection par Champ

```java
@ApplicationScoped
public class OrderService {
    @Inject
    private PaymentService paymentService;

    @Inject
    private Logger logger;
}
```

- ✅ Syntaxe concise
- ❌ Difficile à tester (pas d'accès direct)
- ❌ Dépendances non finales

L'injection par **setter** (`@Inject` sur un setter) permet des dépendances optionnelles, mais le bean peut se trouver dans un état incomplet ; elle est rarement utilisée.

### Injection par Type

CDI injecte par défaut en se basant sur le **type** de l'attribut ou du paramètre.

```java
public interface PaymentService {
    void processPayment(double amount);
}

@ApplicationScoped
public class CreditCardPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("Paiement par carte : " + amount);
    }
}

@ApplicationScoped
public class OrderService {
    @Inject
    private PaymentService paymentService; // Type = PaymentService

    public void checkout(double amount) {
        paymentService.processPayment(amount);
    }
}
```

**Règle CDI** : le conteneur cherche un bean dont le type correspond à `PaymentService`.

### Annotation `@Named`

L'annotation `@Named` permet de donner un nom à un bean pour le référencer dans Expression Language (EL), notamment dans les pages JSF.

```java
@Named("cart")  // Accessible via #{cart} dans JSF
@SessionScoped
public class ShoppingCartBean implements Serializable {
    private List<Item> items = new ArrayList<>();

    public void addItem(Item item) {
        items.add(item);
    }

    public List<Item> getItems() {
        return items;
    }
}
```

**Utilisation dans JSF :**

```xml
<h:dataTable value="#{cart.items}" var="item">
    <h:column>#{item.name}</h:column>
</h:dataTable>
```

**Nom par défaut** : si `@Named` est sans valeur, le nom est celui de la classe avec la première lettre minuscule (ex. `ShoppingCartBean` → `shoppingCartBean`).

### Ce qui est Injecté : le contrat d'injection

Lorsqu'on injecte une dépendance avec `@Inject`, CDI recherche un bean correspondant au **contrat d'injection** : **contrat = Type + Qualifiers + `@Default`**. Le bean injecté doit :

1. ✅ Implémenter ou étendre le type spécifié
2. ✅ Posséder tous les qualifiers spécifiés
3. ✅ Avoir un scope approprié
4. ✅ Être activé (pas désactivé par un `@Alternative` non activé — voir la leçon avancée, *beans alternatifs*)

**Cas particulier : si aucun qualifier n'est spécifié**, toute injection utilise par défaut le qualifier implicite `@Default` (détaillé dans la section *Qualifiers* ci-dessous).

### Initializer Methods

Les **initializer methods** sont des méthodes annotées `@Inject` : `@Inject` peut donc cibler non seulement les champs et le constructeur, mais aussi des méthodes. CDI établit un ordre d'initialisation fixé (constructeur, puis injection des champs et des méthodes, enfin `@PostConstruct`) ; l'ordre complet et ses détails sont traités en partie avancée.

> **TP** : la mise en place de bout en bout d'une première application CDI (projet Quarkus,
> bean, injection par constructeur, scopes, qualifiers, événements) fait l'objet du TP
> dédié : « Premiers pas avec CDI » (`02_java_CDI_P_cdi-injection.qmd`).

## Conflits de Dépendances

Que se passe-t-il si **plusieurs implémentations** d'une même interface existent ?

```java
public interface PaymentService {
    void processPayment(double amount);
}

@ApplicationScoped
public class PayPalPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("💳 Paiement PayPal : " + amount);
    }
}

@ApplicationScoped
public class CreditCardPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("💳 Paiement Carte : " + amount);
    }
}

@ApplicationScoped
public class OrderService {
    @Inject
    private PaymentService paymentService;  // ❌ Ambiguïté !
}
```

**CDI lève une exception** : `AmbiguousResolutionException` — deux beans correspondent au type `PaymentService`, CDI ne sait pas lequel injecter.

**Trois solutions** :

1. **Qualifiers** : distinguer les implémentations (recommandé — voir ci-dessous).
2. **Alternatives** : activer/désactiver des implémentations via configuration.
3. **Producers** : contrôler finement la création.

## Qualifiers

Les **qualifiers** permettent de distinguer différentes implémentations d'une même interface.

### Création d'un Qualifier

Un qualifier est une annotation custom, déclarée avec `@Qualifier` :

```java
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface PayPal {}

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface CreditCard {}
```

### Application du Qualifier

```java
@PayPal
@ApplicationScoped
public class PayPalPaymentService implements PaymentService {
    @Override
    public void processPayment(double amount) {
        System.out.println("💳 Paiement PayPal : " + amount);
    }
}
```

### Injection Qualifiée

```java
@ApplicationScoped
public class OrderService {
    @Inject
    @PayPal
    private PaymentService paypalService;

    @Inject
    @CreditCard
    private PaymentService creditCardService;
}
```

### Qualifier `@Default`

Lorsqu'aucun qualifier n'est spécifié, CDI utilise le qualifier implicite **`@Default`** : `@Inject private PaymentService service;` est équivalent à `@Inject @Default private PaymentService service;`.

**Règle importante** : un bean portant un qualifier personnalisé (ex. `@PayPal`) **n'a pas** `@Default` ; pour qu'un bean ait `@Default`, il ne doit porter aucun qualifier explicite.

> Les qualifiers avec valeurs (String/`value`) et les combinaisons de qualifiers → partie avancée.

## Événements

Les **événements** permettent une communication découplée entre composants : un émetteur envoie un événement, des observateurs réagissent, sans que l'émetteur connaisse les observateurs.

### Principe

```text
Émetteur → Event<T> → CDI Container → @Observes/@ObservesAsync → Observateurs
```

**Avantages** : ✅ couplage faible (émetteur ≠ observateurs) · ✅ extensibilité (ajouter des observateurs sans modifier l'émetteur) · ✅ communication asynchrone possible.

### Émission d'Événements

```java
// Classe d'événement (POJO)
public class OrderCreatedEvent {
    private final String orderId;
    private final double amount;

    public OrderCreatedEvent(String orderId, double amount) {
        this.orderId = orderId;
        this.amount = amount;
    }

    // getters
}

@ApplicationScoped
public class OrderService {
    @Inject
    private Event<OrderCreatedEvent> orderEvent;

    public void createOrder(String orderId, double amount) {
        // Logique métier...
        orderEvent.fire(new OrderCreatedEvent(orderId, amount));
    }
}
```

### Observation Synchrone (`@Observes`)

Les observateurs synchrones sont invoqués dans le **même thread** que l'émetteur :

```java
@ApplicationScoped
public class EmailNotificationService {
    public void onOrderCreated(@Observes OrderCreatedEvent event) {
        System.out.println("📧 Envoi email pour commande " + event.getOrderId());
    }
}
```

**Résultat :**

```console
Création commande ORD-123
📧 Envoi email pour commande ORD-123
📦 Mise à jour stock pour commande ORD-123
```

**⚠️ Attention** : si un observateur lève une exception, les suivants ne sont pas exécutés.

### Observation Asynchrone (`@ObservesAsync`)

Les observateurs asynchrones sont invoqués dans un **thread séparé** (on peut aussi émettre de façon asynchrone avec `fireAsync`) :

```java
@ApplicationScoped
public class AnalyticsService {
    public void onOrderCreated(@ObservesAsync OrderCreatedEvent event) {
        // Traitement long (thread séparé)
        System.out.println("📊 Traitement analytics");
    }
}
```

> Les événements qualifiés et l'observation conditionnelle → partie avancée. L'observation d'événements à des phases transactionnelles (`TransactionPhase`) relève de la leçon 03 (Transactions) et de la partie avancée.

## Résumé

### Concepts Fondamentaux

- **Bean CDI** = Types + Qualifiers + Scope + Implémentation
- **Injection** = `@Inject` (constructeur, field, setter)
- **Scopes** = `@ApplicationScoped`, `@RequestScoped`, `@SessionScoped`, `@Dependent`

### Gestion des Dépendances

- **Qualifiers** : distinguer les implémentations (`@Qualifier`)
- **`@Default`** : qualifier implicite si aucun qualifier personnalisé
- **`@Named`** : nommer un bean pour l'EL (JSF)

### Événements

- **Événements** : communication découplée (`@Observes`, `@ObservesAsync`)

> Les concepts avancés (stereotypes, producers, intercepteurs, décorateurs) et les implémentations (Weld, Quarkus ArC) → partie avancée.

## Bonnes Pratiques

### 1. Préférez l'Injection par Constructeur

Dépendances finales, explicites et faciles à tester — voir la section « Injection de Dépendances ».

### 2. Utilisez les Scopes Appropriés

```java
// ✅ Service partagé = @ApplicationScoped
@ApplicationScoped
public class ConfigurationService { }

// ✅ Données de requête = @RequestScoped
@RequestScoped
public class OrderFormBean { }
```

### 3. Nommez les Qualifiers de Manière Descriptive

```java
// ✅ Descriptif
public @interface FastProcessing { }

// ❌ Peu clair
public @interface Q1 { }
```

### 4. Évitez les Cycles de Dépendances

```java
// ❌ ERREUR : Cycle A → B → A
@ApplicationScoped
public class ServiceA {
    @Inject
    private ServiceB serviceB;  // B dépend de A !
}

// ✅ SOLUTION : refactorer le découplage (ou produire le bean ailleurs)
```

> Les bonnes pratiques avancées (`@Alternative`, producers/`@Disposes`, documentation des stereotypes) → partie avancée.

## Ressources

### Documentation Officielle

- 📚 [Jakarta CDI Specification](https://jakarta.ee/specifications/cdi/4.0/)
- 📖 [Weld Documentation](https://docs.jboss.org/weld/reference/latest/en-US/html/)
- 🚀 [Quarkus CDI Guide](https://quarkus.io/guides/cdi)

### Exemples

- 💻 [Sample CDI Project](https://github.com/ebpro/sample-cdi)
- 💻 [Quarkus Quickstarts](https://github.com/quarkusio/quarkus-quickstarts)

### Tutoriels

- 🎓 [Jakarta EE Tutorial - CDI](https://jakarta.ee/learn/docs/jakartaee-tutorial/current/)
- 🎥 [Quarkus CDI Extensions](https://www.youtube.com/c/Quarkusio)

### Outils

- 🔧 [CDI TCK](https://github.com/jakartaee/cdi-tck) — Test Compatibility Kit
- 🛠️ [Arquillian](https://arquillian.org/) — Testing CDI applications

> **Partie avancée** : les approfondissements (qualifiers avec valeurs, beans alternatifs,
> producers, intercepteurs, décorateurs, événements avancés, stereotypes) sont traités
> dans la leçon optionnelle (`02_java_CDI_L_advanced.qmd`).
