AOP mit AspectJ

Die aspektorientierte Programmierung (AOP) bietet zusätzlich zur objektorientierten Programmierung (OOP) eine neue Möglichkeit der Modularisierung von Software. In der OOP werden Funktionalität durch Methoden, Klassen und Pakete modularisiert. Klassen fassen Methoden zu einer Einheit mit einer Verantwortlichkeit zusammen. Pakete gruppieren zusammengehörige Klassen. Diese Modularisierung fokusiert auf die Geschäftslogik von Software.

Für querschnittliche Funktionalität wie Prokollierung, Fehlerbehandlung oder Sicherheit gibt es außer der Nutzung von Schnittstellen (Java: Interfaces) keine Möglichkeit der Modularisierung in OOP. Hier bietet AOP mit Aspekten eine neue Möglichkeit der Modularisierung. Die querschnittliche Funktionalität wird nicht in den Klassen der Geschäftslogik implementiert, sondern in sogenannten Aspekten. Ein Aspekt ist eine besonderen Klasse, deren Funktionalität, zur Übersetzungszeit (Compile Time Weaving) oder zur Laufzeit (Load Time Weaving) mit den Klassen der Geschäftslogik verbunden wird.

Ein Aspekt definiert die Operationen die an einer identifizierbaren Programmstelle (Joinpoint) eingebunden werden. Ein Joinpoint kann unter anderen der Aufruf einer Methode, der Zugriff auf eine Klassenvariable oder das Werfen einer Ausnahme sein. Da Aspekte querschnittliche Funktionalität definieren, werden Mengen von Joinpoints zu Pointcuts zusammengestellt. Pointcuts können aus mehren Joinpoints durch die Verwendung von Wildcards und logischen Operatoren zusammengestellt werden. Für einen Pointcut werden die auszuführenden Operationen durch einen Advice implementiert.

Ein Advice wird entweder vor oder nach einem Joinpoint ausgeführt, also zum Beispiel vor (before) oder nach (after) dem Ausführen einer Methode. Zudem besteht die Möglichkeit das ein Advice einen Joinpoint umschließt (around). Damit lässt sich zum Beispiel der Aufruf einer Methode manipulieren (Parameter oder Rückgabe anpassen) oder gar der Methodenaufruf verhindern (Sicherheitsaspekt).

AspectJ

Hier möchte ich AspectJ kurz am Beispiel vom Logging vorstellen. Unter Maven werden die Aspekt im Verzeichnis src/main/aspect/ abgelegt. Der Aspekt TraceAspect protokolliert jeden Aufruf jeder Methode jeder Klasse.

Konfiguration von Maven

[...]
 
<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.6</version>
    </dependency>
</dependencies>
 
<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.7</version>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <complianceLevel>1.8</complianceLevel>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
 
[...]

Aspekt zur Protokollierung

import org.aspectj.lang.Signature;
 
import java.util.logging.Level;
import java.util.logging.Logger;
 
public aspect TraceAspect {
 
    private Logger logger = Logger.getLogger("trace");
 
    TraceAspect() {
        logger.setLevel(Level.ALL);
    }
 
    pointcut traceMethod(): (execution(*.new(..)) || execution(* *.*(..))) && !within(TraceAspect);
 
    before(): traceMethod() {
        Signature signature = thisJoinPointStaticPart.getSignature();
        logger.logp(Level.INFO, signature.getDeclaringType().getName(), signature.getName(), "Entering");
    }
 
}

Beispiel für einen before-Aspekt

public pointcut inventarAdd(Medium medium): call(void Inventory.addItem(Medium)) && args(medium);
 
before(Medium medium): inventarAdd(medium) {
    System.out.println("Item " + medium + " in Inventory added");
}

Beispiel für einen after-Aspekt

public pointcut getTitle(): call(void getTitle());
 
after() returning(String title): call(String getTitle()) {
    System.out.println("Rückgabe der getTitle Methode ist: " + title);
}

Beispiel für ein around-Aspekt

void around(ShoppingCart shoppingCart, Inventory inventory, Medium medium):
        call(* *ShoppingCartItem(ShoppingCart, Inventory, Medium)) && args(shoppingCart, inventory, medium) {
    try {
        proceed(shoppingCart, inventory, medium);
    } catch (Exception ex) {
        System.out.println("Errors occurs in ShoppingCart");
    }
}

Maven-Report

Um die Übersicht wo welcher Aspekt eingebunden wird, nicht zu verlieren kann mit Maven ein Report erstellt werden. Dieser Report erweitert die JavaDoc-Dokumentation um die Querverweise welche Methoden durch welche Aspekte ergänzt werden und wo welche Aspekte eingebunden werden.

<reporting>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.7</version>
            <configuration>
                <privateScope>true</privateScope>
                <complianceLevel>1.8</complianceLevel>
            </configuration>
            <reportSets>
                <reportSet>
                    <reports>
                        <report>aspectj-report</report>
                    </reports>
                </reportSet>
            </reportSets>
        </plugin>
    </plugins>
</reporting>

Weiterführende Literatur

Schreibe einen Kommentar

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