CodeGym /Kurse /JAVA 25 SELF /Ausnahmeverkettung (Exception Chaining)

Ausnahmeverkettung (Exception Chaining)

JAVA 25 SELF
Level 24 , Lektion 2
Verfügbar

1. Einleitung

In dieser Lektion behandeln wir eine wichtige Technik im Umgang mit Ausnahmen – die Ausnahmeverkettung (exception chaining). Diese Technik ermöglicht es, Informationen über die ursprüngliche Fehlerursache nicht zu verlieren, selbst wenn Sie eine Ausnahme in eine andere „wrappen“.

In realen Anwendungen kommt es oft vor, dass ein Fehler tief im Aufruf-Stack entsteht – zum Beispiel bei der Arbeit mit der Datenbank, dem Dateisystem oder dem Netzwerk. Angenommen, Sie haben eine Methode, die auf die Datenbank zugreift und eine SQLException auslösen kann. Auf der Ebene der Business-Logik möchten Sie den Code jedoch nicht mit technischen Details „verschmutzen“ und bevorzugen es, eine eigene Ausnahme auszulösen, zum Beispiel UserManagementException.

Was passiert, wenn Sie einfach eine neue Ausnahme auslösen?

try {
    // irgendetwas mit der Datenbank
} catch (SQLException e) {
    throw new UserManagementException("Fehler bei der Benutzerverwaltung");
}

Problem:
In diesem Fall gehen die Informationen darüber, was genau in der Datenbank passiert ist (und der Aufruf-Stack!), verloren. Im Log sehen Sie nur die UserManagementException, die Ursache – unbekannt.

2. Lösung: ursprüngliche Ausnahme „wrappen“ (Chaining)

Java erlaubt es, eine Ausnahme in eine andere zu „wrappen“, indem man die ursprüngliche Ausnahme als Ursache (cause) an den Konstruktor der neuen Ausnahme übergibt. Das nennt man Ausnahmeverkettung.

Wie geht das?

Die meisten Standard- und benutzerdefinierten Ausnahmen haben einen Konstruktor, der einen zweiten Parameter akzeptiert – Throwable cause:

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

Verwendung:

try {
    // irgendetwas mit der Datenbank
} catch (SQLException e) {
    throw new UserManagementException("Fehler bei der Benutzerverwaltung", e);
}

Wenn Sie sich jetzt den Stacktrace (printStackTrace()) ansehen, sehen Sie sowohl Ihre Ausnahme als auch die gesamte Kette bis zur eigentlichen Ursache!

3. Wie man die Ursache einer Ausnahme ermittelt

Jedes Objekt vom Typ Throwable hat die Methode getCause(), die die ursprüngliche Ausnahme zurückgibt (oder null, wenn es keine gibt).

Beispiel:

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

Wozu ist das gut?

  • Zum Debuggen: Sie sehen nicht nur „was schiefgelaufen ist“ auf der obersten Ebene, sondern auch, wo genau der Fehler tiefer im Stack aufgetreten ist.
  • Fürs Logging: Sie können die gesamte Fehlerkette ins Log schreiben.
  • Für die Informationsweitergabe zwischen den Schichten der Anwendung: Die Business-Schicht kann die technische Ausnahme in ihre eigene „wrappen“, ohne Details zu verlieren.

4. Beispiel: Ausnahmeverkettung in einer realen Anwendung

Angenommen, Sie haben eine Methode, die einen Benutzer aus der Datenbank lädt:

public User loadUser(String username) throws UserManagementException {
    try {
        // Code, der eine SQLException auslösen kann
        // ...
    } catch (SQLException e) {
        throw new UserManagementException("Benutzer konnte nicht geladen werden: " + username, e);
    }
}

Dabei ist UserManagementException Ihre eigene Ausnahme:

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

Was passiert im Fehlerfall?

  • In den Logs sind sowohl Ihre Ausnahme als auch die ursprüngliche SQLException mit allen Details sichtbar.
  • Bei Bedarf können Sie über getCause() auf die Wurzelursache zugreifen.

5. Wie ein Stacktrace bei Ausnahmeverkettung aussieht

Beispielausgabe:

UserManagementException: Benutzer konnte nicht geladen werden: rutger
    at UserService.loadUser(UserService.java:15)
    ...
Caused by: java.sql.SQLException: Connection refused
    at ...

Hier ist alles zu sehen: die vollständige Aufrufkette, wo der Business-Fehler entstanden ist, welche technische Ausnahme die Ursache war.

6. Praxis: Ausnahmeverkettung implementieren

Schritt 1. Eigene Ausnahme erstellen:

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

Schritt 2. Verkettung verwenden:

try {
    // etwas Riskantes
} catch (SQLException e) {
    throw new UserManagementException("Fehler bei der Arbeit mit der Datenbank", e);
}

Schritt 3. Behandlung auf oberster Ebene:
Auf der obersten Ebene des Programms fangen wir unsere eigene Ausnahme ab und geben die Fehlermeldung zusammen mit der gesamten Ursachen-Kette aus.

public class Main {
    public static void main(String[] args) {
        try {
            runUserManagement();
        } catch (UserManagementException e) {
            System.err.println("Es ist ein Fehler aufgetreten: " + e.getMessage());
            // Ursachen-Kette ausgeben
            Throwable cause = e.getCause();
            while (cause != null) {
                System.err.println("Ursache: " + cause.getMessage());
                cause = cause.getCause();
            }
        }
    }

    private static void runUserManagement() throws UserManagementException {
        try {
            // Simulation eines Datenbankfehlers
            throw new SQLException("Keine Verbindung zur Datenbank");
        } catch (SQLException e) {
            throw new UserManagementException("Fehler bei der Arbeit mit der Datenbank", e);
        }
    }
}

7. Typische Fehler beim Arbeiten mit Ausnahmeverkettung

Fehler Nr. 1: Sie lösen eine neue Ausnahme ohne cause aus.

catch (SQLException e) {
    throw new UserManagementException("Fehler", /* kein cause! */);
}

Schlecht: Informationen über die Wurzelursache gehen verloren.

Fehler Nr. 2: In Ihrer eigenen Ausnahme ist kein Konstruktor mit cause implementiert.
Wenn Ihre Ausnahmeklasse keinen Konstruktor mit Throwable cause hat, können Sie die Ursache nicht übergeben – Sie müssen ihn manuell hinzufügen.

Fehler Nr. 3: Sie fangen die Ausnahme ab und schlucken sie, ohne sie weiterzugeben.

catch (SQLException e) {
    // Nur loggen und schweigen
}

Schlecht: Der Fehler „geht verloren“, das Programm läuft fehlerhaft weiter.

try {
    userService.loadUser("rutger");
} catch (UserManagementException e) {
    System.err.println("Fehler: " + e.getMessage());
    if (e.getCause() != null) {
        System.err.println("Wurzelursache: " + e.getCause());
    }
    e.printStackTrace();
}
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION