Automatisches Aufräumen von Ressourcen in Spring

Eine der vielen Funktionen des Spring-Frameworks ist die Fähigkeit, Ressourcen beim Herunterfahren der Anwendung automatisch aufräumen zu lassen. Dieser Artikel verschafft einen Überblick über die Möglichkeiten die es gibt, und wie man diese nutzt.

Der Lebenszyklus von Spring-Beans

Die Verwaltung aller Objektinstanzen einer Spring-Anwendung übernimmt der sogenanten IoC-Container. Im Spring-Jargon werden die vom Container verwalteten Anwendungsobjekte „Beans“ genannt. Der Container stellt einen Lebenszyklus bereit, über den die Beans instanziiert, initialisiert und auch wieder „zerstört“ werden. Der Entwickler liefert hierfür lediglich eine Konfiguration, um alles weitere kümmert sich der Container.

Beispiel DbRepository

Angenommen wir haben eine Spring-Anwendung, die ein DbRepository für Datenbankabfragen nutzt. Fährt die Anwendung herunter sollte sie sicherstellen, dass das Repository hinter sich aufräumt und beispielsweise die Connections im Connection Pool schließt. Zu diesem Zweck hat das DbRepository eine Methode, die z.B. close() oder destroy() heißt. Nun gibt es mehrere Möglichkeiten dafür zu sorgen, dass der Spring-Container diese Methode beim Beenden der Anwendung aufruft:

Via XML

Seit jeher unterstützt Spring die Bean-Konfiguration in XML. Hier gibt man im XML-Attribut destroy-method den Namen der Methode an, die das Aufräumen übernimmt:

Diese Konfiguration bewirkt, dass beim Herunterfahren der Anwendung die Methode cleanUp() der DbRepository-Instanz aufgerufen wird.

Via Interface

Stattdessen ist es auch möglich, die Klasse ein entsprechendes Interface implementieren zu lassen. Hier gibt es zwei Möglichkeiten:

Interface DisposableBean

Dies ist ein Spring-spezifisches Interface, das nur eine Methode namens void destroy() besitzt. Die Definition unserer DbRepository-Klasse sähe damit so aus:

Weitere Konfigurationen sind nicht notwendig. Der Spring-Container erkennt automatisch das implementierte Interface, und ruft die destroy()-Methode beim Herunterfahren auf.

Interface (Auto)Closeable

Denselben Effekt hat es, wenn die Bean-Klasse das Interface Closeable oder AutoCloseable implementiert. Beiden Interfaces gemein ist die Methode void close(), die sich nur im throws-Statement ihrer Signatur unterscheidet. Auch hier erkennt der Spring-Container das implementierte Interface, und ruft entsprechend beim Shutdown die close()-Methode auf.

Hier unser DbRepository nochmal mit implementiertem AutoCloseable-Interface:

Diese beiden Interfaces sind nicht von Spring abhängig, da sie zur Java-Standardbibliothek gehören. Implementierungen des AutoCloseable-Interfaces können zudem im try-with-resources-Statement von Java verwendet werden.

Via Annotation @PreDestroy

Diese Variante benötigt weder XML-Konfiguration noch Interface-Implementierung. Stattdessen wird die Aufräum-Methode einfach mit der Spring-eigenen Annotation @PreDestroy markiert:

Wie das Beispiel zeigt können auch mehrere Methoden annotiert werden. In diesem Fall werden alle mit @PreDestroy versehenen Methoden aufgerufen. Eine spezielle Ausführungsreihenfolge wird nicht garantiert.

Via @Bean-Konfiguration

Alle zuvor beschriebenen Varianten funktionieren sowohl in Objekten von @Component-Klassen, als auch in Instanzen die mittels einer @Bean-annotierten Factorymethode erzeugt werden.

Im folgenden werden die Möglichkeiten beschrieben, die sich auf die Konfiguration mit @Bean beschränken.

Angabe der Methode in der Annotation

In der DbRepository-Klasse ist in diesem Fall nichts zu tun. Stattdessen erfolgt die Konfiguration innerhalb einer Konfigurationsklasse, die mit @Configuration annotiert ist:

Im Attribut destroyMethod der @Bean-Annotation wird der Name der Aufräum-Methode angegeben, im obigen Beispiel ist dies die Methode closeConnectionPool().

Automatische Ermittlung der Methode

Das destroyMethod-Attribut in @Beans ist nicht zwingend erforderlich, man kann es daher auch weglassen:

In diesem Fall versucht Spring, die Aufräum-Methode automatisch zu ermitteln. Existiert in DbRepository eine parameterlose Methode mit Namen close() oder shutdown() die public ist, wird diese automatisch als Aufräum-Methode verwendet.

Existiert eine solche Methode, die allerdings nicht automatisch von Spring aufgerufen werden soll, kann dies wie folgt per Leerstring konfiguriert werden:

Hinweis: Hiermit wird nur das automatische Aufrufen der shutdown()- bzw. close()-Methode unterbunden, unabhängig von einer Implementierung des (Auto)Closeable-Interfaces. Implementiert die Klasse das DisposableBean-Interface, wird die destroy()-Methode dennoch ausgeführt.

Die Qual der Wahl

Für welche der Möglichkeiten sollte man sich nun entscheiden? Mitunter ist das sicherlich Geschmackssache, bzw. abhängig von den Vorgaben im Projekt.

Auf jeden Fall rate ich davon ab, den Methodennamen String-basiert zu konfigurieren, wie es in der XML-Konfiguration oder im destroyMethod-Attribut von @Bean der Fall ist. Denn das ist weder typsicher noch Refactoring-resistent. Wird die Methode umbenannt und in der Konfiguration nicht nachgezogen, kommt es zu einem Laufzeitfehler.

Auch die automatische Ermittlung über @Bean ohne Angabe eines Methodennamens würde ich persönlich nicht einsetzen. Denn wird die close()– oder shutdown()-Methode später umbenannt, wird keine Aufräum-Methode mehr aufgerufen, möglicherweise ohne dass es auffällt, was ggf. ein Ressourcen-Leck zur Folge haben kann. Dasselbe passiert wenn die Klasse nicht mehr über eine @Bean-Methode instanziert wird sondern als @Component über den Komponenten-Scan. Auch in diesem Fall wird die close()– bzw. shutdown()-Methode nach dem Refactoring nicht mehr aufgerufen.

Diese Probleme hat man in der Interface-Variante nicht. Ein Umbenennen der Methode würde sowohl bei DisposableBean als auch bei (Auto)Closeable sofort durch einen Kompilierfehler auffallen, da die Klasse das Interface dann nicht mehr implementiert. Zudem wird die Aufräum-Methode bei Implementierung von einem der Interfaces in jedem Fall ausgeführt. Muss ich zwischen den Interfaces wählen, wird meine Wahl auf AutoCloseable fallen. Dadurch ist meine Klasse weniger vom Spring-Framework abhängig, zudem kann sie außerhalb des Spring-Kontexts im try-with-resources-Statement verwendet werden. Nachteil dieser Variante ist die recht versteckte Dokumentation dieses Features im Javadoc von DisposableBean.

Die Nutzung von DisposableBean macht die Klasse zwar abhängig vom Spring-Framework, ist dafür aber explizieter und förderlicher für das Verstehen des Codes. Dasselbe gilt für die Nutzung der @PreDestroy-Annotation. Hier gilt es im Einzelfall abzuwägen, ob man dafür eine hohe Kopplung des Anwendungscodes an ein Framework in Kauf nehmen will, oder nicht.

Zurück zur Übersicht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*Pflichtfelder

*