CodeGym /Cours /JAVA 25 SELF /Chaînage d'exceptions (Exception Chaining)

Chaînage d'exceptions (Exception Chaining)

JAVA 25 SELF
Niveau 24 , Leçon 2
Disponible

1. Introduction

Dans ce cours, nous allons examiner une technique importante de gestion des exceptions — le chaînage d'exceptions (exception chaining). Cette technique permet de ne pas perdre l'information sur la cause première de l'erreur, même si vous « enveloppez » une exception dans une autre.

Dans des applications réelles, il arrive souvent qu’une erreur se produise profondément dans la pile d’appels — par exemple lors de l’utilisation d’une base de données, du système de fichiers ou du réseau. Supposons que vous avez une méthode qui interroge la base de données et peut lever une SQLException. Mais au niveau de la logique métier, vous ne voulez pas « encombrer » le code avec des détails techniques et vous préférez lever votre propre exception, par exemple UserManagementException.

Que se passe-t-il si vous levez simplement une nouvelle exception ?

try {
    // quelque chose avec la base de données
} catch (SQLException e) {
    throw new UserManagementException("Erreur lors de la gestion des utilisateurs");
}

Problème :
Dans ce cas, l’information sur ce qui s’est précisément produit dans la base de données (et la pile d’appels !) est perdue. Dans le log, vous ne verrez que UserManagementException, sans connaître la cause.

2. Solution : envelopper l’exception d’origine (chaining)

Java permet d’« envelopper » une exception dans une autre en passant l’exception d’origine comme cause (cause) au constructeur de la nouvelle exception. C’est ce que l’on appelle le chaînage d'exceptions.

Comment faire ?

La plupart des exceptions standard ou personnalisées possèdent un constructeur acceptant un second paramètre — Throwable cause :

public UserManagementException(String message, Throwable cause) {
    super(message, cause);
}

Utilisation :

try {
    // quelque chose avec la base de données
} catch (SQLException e) {
    throw new UserManagementException("Erreur lors de la gestion des utilisateurs", e);
}

Désormais, si vous regardez la pile d’appels (printStackTrace()), vous verrez à la fois votre exception et toute la chaîne jusqu’à la cause première !

3. Comment obtenir la cause d’une exception

Tout objet de type Throwable possède une méthode getCause() qui renvoie l’exception d’origine (ou null si elle n’existe pas).

Exemple :

try {
    // ...
} catch (UserManagementException e) {
    Throwable cause = e.getCause();
    if (cause != null) {
        System.out.println("Cause racine : " + cause);
    }
    e.printStackTrace();
}

À quoi cela sert-il ?

  • Pour le débogage : vous voyez non seulement « ce qui s’est mal passé » au niveau supérieur, mais aussi où exactement l’erreur s’est produite dans la profondeur de la pile.
  • Pour la journalisation : vous pouvez consigner toute la chaîne d’erreurs dans les journaux.
  • Pour transmettre l’information entre les couches de l’application : la couche métier peut « envelopper » une exception technique dans la sienne sans perdre de détails.

4. Exemple : chaînage d’exceptions dans une application réelle

Supposons que vous avez une méthode qui charge un utilisateur à partir de la base de données :

public User loadUser(String username) throws UserManagementException {
    try {
        // Code pouvant lever SQLException
        // ...
    } catch (SQLException e) {
        throw new UserManagementException("Échec du chargement de l’utilisateur : " + username, e);
    }
}

UserManagementException est votre propre exception :

public class UserManagementException extends Exception {
    public UserManagementException(String message, Throwable cause) {
        super(message, cause);
    }
}

Que se passera-t-il en cas d’erreur ?

  • Dans les journaux, vous verrez à la fois votre exception et la SQLException d’origine avec tous les détails.
  • Si nécessaire, vous pouvez accéder à la cause première via getCause().

5. À quoi ressemble la pile d’appels avec un chaînage d’exceptions

Exemple de sortie :

UserManagementException: Impossible de charger l’utilisateur : john
    at UserService.loadUser(UserService.java:15)
    ...
Caused by: java.sql.SQLException: Connection refused
    at ...

On y voit tout : la chaîne complète des appels, où l’erreur métier est survenue, et quelle exception technique en est la cause.

6. Pratique : implémentons le chaînage d’exceptions

Étape 1. Créer sa propre exception :

public class UserManagementException extends Exception {
    public UserManagementException(String message) {
        super(message);
    }
    public UserManagementException(String message, Throwable cause) {
        super(message, cause);
    }
}

Étape 2. Utiliser le chaînage :

try {
    // quelque chose de risqué
} catch (SQLException e) {
    throw new UserManagementException("Erreur lors des opérations sur la base de données", e);
}

Étape 3. Traitement au niveau le plus haut :
Au niveau le plus haut du programme, nous interceptons notre propre exception et affichons le message d’erreur ainsi que toute la chaîne des causes.

public class Main {
    public static void main(String[] args) {
        try {
            runUserManagement();
        } catch (UserManagementException e) {
            System.err.println("Une erreur s’est produite : " + e.getMessage());
            // Nous affichons la chaîne des causes
            Throwable cause = e.getCause();
            while (cause != null) {
                System.err.println("Cause : " + cause.getMessage());
                cause = cause.getCause();
            }
        }
    }

    private static void runUserManagement() throws UserManagementException {
        try {
            // simulation d’une erreur de base de données
            throw new SQLException("Aucune connexion à la base de données");
        } catch (SQLException e) {
            throw new UserManagementException("Erreur lors des opérations sur la base de données", e);
        }
    }
}

7. Erreurs typiques lors de l’utilisation du chaînage d’exceptions

Erreur n° 1 : vous levez une nouvelle exception sans cause.

catch (SQLException e) {
    throw new UserManagementException("Erreur", /* pas de cause ! */);
}

Mauvais : l’information sur la cause racine est perdue.

Erreur n° 2 : vous n’avez pas implémenté de constructeur avec cause dans votre exception.
Si votre classe d’exception n’a pas de constructeur avec Throwable cause, vous ne pourrez pas transmettre la cause — il faudra l’ajouter manuellement.

Erreur n° 3 : vous interceptez et étouffez l’exception sans la propager.

catch (SQLException e) {
    // On se contente de journaliser et on n’en fait rien
}

Mauvais : l’erreur se « perd », le programme continue de fonctionner de manière incorrecte.

try {
    userService.loadUser("john");
} catch (UserManagementException e) {
    System.err.println("Erreur : " + e.getMessage());
    if (e.getCause() != null) {
        System.err.println("Cause racine : " + e.getCause());
    }
    e.printStackTrace();
}
1
Mission
JAVA 25 SELF, niveau 24, leçon 2
Bloqué
Détective des données : identifier la cause première d'une défaillance
Détective des données : identifier la cause première d'une défaillance
1
Mission
JAVA 25 SELF, niveau 24, leçon 2
Bloqué
Chemin vers l'erreur : enchaînement des défaillances dans le système de reporting
Chemin vers l'erreur : enchaînement des défaillances dans le système de reporting
1
Mission
JAVA 25 SELF, niveau 24, leçon 2
Bloqué
"Erreur externe" avec "cause racine" : Investigation des causes
"Erreur externe" avec "cause racine" : Investigation des causes
1
Mission
JAVA 25 SELF, niveau 24, leçon 2
Bloqué
Catastrophe spatiale : chaîne de défaillances multi-niveaux
Catastrophe spatiale : chaîne de défaillances multi-niveaux
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION