07.09.2011

Spring AOP - Logging auf Methodenlevel leicht gemacht

Richtiges Logging innerhalb der Anwendung ist eine Kunst für sich. Das Logging an sich sollte minimal-invasiv sein, so dass der Entwickler in der normalen Programmiertätigkeit nicht eingeschränkt oder behindert wird. Das Logging sollte zentral definiert und vor allem auch zentral konfigurierbar sein.

Eine Möglichkeit Logging zu realisieren ist die Nutzung der aspektorientierten Programmierung. Dabei wird der bestehende Code weitesgehend unangetastet gelassen und nur eine weitere Ebene über den bestehenden Code gelegt. Die funktionalen Aufgaben (Core-Level-Concerns) und damit gleichzeitig die Kernaufgaben der Software bleiben im Mittelpunkt und werden nicht mit den bestehenden technischen Randbedingungen (System-Level-Concerns) vermischt.

Gerade Spring bietet mit der einfachen Integration von AspectJ eine einfach zu konfigurierende Möglichkeit ein systemweites, losgelöstes Logging zu realisieren. Und genau um dieses Thema, visualisiert an einem kleinen Beispiel, soll es in diesem Eintrag gehen. Wie immer gilt, dass die Ausschnitte vergrößert werden können, sollten diese zu klein sein.

Um den Overhead zur Erstellung eines Projektes so klein wie möglich zu gestalten, wird komplett auf eine Verwaltung der Dependencies durch Maven gesetzt. Daher an dieser Stelle nur die Erläuterung der berücksichtigten Dependencies.

Für die Basiskonfiguration von Spring wird die folgende Dependency benötigt.

Spring-Context Dependency



Sollte diese in dem Standard-Repository von Maven nicht vorhanden sein, so kann auf das folgende Repository ausgewichen werden.
Alternatives Repository für Spring Dependencies
Für die Nutzung von AspectJ werden zwei Dependencies benötigt. Zum einen AspectJ selbst, aber zum anderen auch CGLIB zur dynamischen Erstellung von Bytecode zur Laufzeit des Programmes.

AspectJ und CGLIB Dependencies
Das Beispiel selbst soll nachher durch einen Test gestartet und damit auch direkt getestet werden. Dafür werden sowohl Spring-Test (inkl. TestNG) als auch JUnit benötigt.

Hinzufügen von JUnit und Spring-Test
Die Ausgaben auf der Konsole erfolgen dann via Log4J und nicht über System.out oder System.err.

Log4j Dependency
Nach einem initialen Build des Projekts und dem Download der Bibliotheken sollten diese innerhalb des Maven Projektes vorhanden sein.

Dependencies im Projekt
Der eigentliche Aufbau des Beispiels ist einfach gehalten, damit die wesentlichen Punkte besser zur Geltung kommen. Das Beispiel besteht aus einer Spring Bean mit implementiertem Interface, der AOP-Komponente, einer Testklasse und den Konfigurationsdateien.

Klassendiagramm für das Beispiel
Die Konfiguration wird, soweit möglich, direkt über Annotationen innerhalb der Klassen realisiert. Um einen Einstieg in das Beispiel zu finden, zuerst die Erläuterung der Implementierung der HelloWorldComponent.

HelloWorldComponent Interface

Die eigentliche Implementierung ist ebenfalls einfach gehalten und Bedarf keiner weiteren Erläuterung.

Implementierung der Komponente
Diese einfache Implementierung bildet die Basis für das Beispiel und wird durch den aspektorientierten Logger vervollständigt. Dieser wird innerhalb der Klasse AopMethodLogger implementiert.

Aspektorientierter Logger auf Methodenlevel
Der Logger wird durch die Annotation @Component Spring Komponente definiert, so dass eine Initialisierung durch das Spring Framework gegeben ist. Damit der Logger gleichzeitig als Aspekt bekannt ist, wird zusätzlich die Annotation @Aspect verwendet, die ebenfalls durch Spring ausgewertet wird. Die eigentliche Methode für das Loggen kann frei definiert werden. In diesem Fall wird der Methode eine Instanz eines ProceedingJoinPoints übergeben, damit auf Eigenschaften der Methode zugegriffen und die Methode nach dem Loggen weiter ausgeführt werden kann. Damit die Methode überhaupt als Aspekt um eine aufgerufene Methode definiert wird, muss mit der Annotation @Around ein Pointcut gekennzeichnet werden. Zusätzlich wird ein Pattern für den Pointcut definiert, damit festgelegt werden kann, wann in diesen Pointcut verzweigt werden soll. In diesem Fall definiert der Ausdruck "execution(* de.sado.examples..*.*(..))" die Ausführung einer jeden Methode innerhalb des Packages de.sado.examples, egal wie die Signatur der Methode definiert ist. Eine sehr gute Beschreibung der weiteren Möglichkeiten findet sich in der offiziellen Referenz zur aktuellen Version von Spring.

Neben der Implementierung der Klassen ist auch die Konfiguration von Spring relevant. Durch die Annotations-gestützte Implementierung bleibt die erforderliche Konfigurationsdatei allerdings recht schlank.

Spring Konfigurationsdatei
Neben der Bean Definition der HelloWorldComponent finden sich hier auch die Scan-Anweisung für das Scannen nach Komponenten und die Konfiguration der AOP Nutzung. Weitere Konfigurationsschritte sind nicht mehr notwendig.

Für den Start des kleinen Beispiel eignet sich ein durch den SpringJUnit4ClassRunner gesteuerter Test. Der Runner garantiert, dass ein interner IoC Container mitgestartet wird und so keine Spring Referenzen gemockt oder per Hand aufgelöst werden müssen. Es kann sogar die Konfiguration der Anwendung wiederverwendet werden, wenn sich dies ergibt.

Test der erstellten Komponente
Durch die Annotation @ContextConfiguration wird die Konfigurationsdatei referenziert. Die Annotation @RunWith sorgt für die Ausführung des Tests mit Hilfe des Spring-internen Testrunners. Somit müssen die Tests nur noch per Maven gestartet werden und schon sieht man das Ergebnis auf der Kommandozeile.


Das Beispiel kann bei Cloudbees ausgecheckt werden. Dort befindet sich ein fertiges Eclipse Projekt mit den entsprechenden Einstellungen.

Keine Kommentare:

Kommentar veröffentlichen