Aspektorientierte Programmierung


... [ Seminar BS, WWW und PS ] ... [ Thema Spring Framework ] ... [ Features: Integration anderer Libraries und Frameworks ] ...

Aspektorientierte Programmierung


Konzept

In einem Programm kann es Aufgaben geben die an mehreren Stellen des Codes vorkommen. Wenn zum Beispiel bestimmte Aktionen geloggt werden sollen muss vor- und hinter der Aktion Code eingefügt werden. Wenn das Logging an verschiedenen Stellen im Programm vorkommen soll, schreibt oder kopiert der Entwickler den gleichen Code an verschiedene Stellen. Andere Szenarien wären das Wiederholen von Datenbank-Zugriffen, Caching oder Authentifizierung. Dieses mehrfache Vorkommen des gleichen Codes an unterschiedlichen Stellen widerspricht dem Konzept "Don't repeat yourself" (DRY-Konzept). Die Aspektorientierung ermöglicht es solche Aufgaben zu kapseln, so dass die Aufgabe nur einmal programmiert, aber an mehreren Stellen ausgeführt werden kann. Wichtig ist es, davor die Begriffe der Aspektorientierung zu klären:
Aspect Eine Aufgabe die an verschiedenen Stellen im Programm benötigt wird, z.B. Logging.
Jointpoint Wohldefinierter Punkt im Programmfluss an dem der Aspekt eingreift. Dies sind zum Beispiel Methodenaufrufe, Feldzugriffe oder Exceptions. In Spring sind nur Methodenaufrufe als Joinpoints möglich.
Advice Aktion eines Aspekts am Joinpoint, also der Code der am Joinpoint ausgeführt wird. Hier würde z.B. das Loggen ausgeführt.
Pointcut Muster, das die für den Aspekt wichtigen Joinpoints zusammenfasst.
Joinpoints können nicht festgelegt werden, da diese nur die Möglichkeiten für einen Advice einzugreifen darstellen. Für die Auswahl der Joinpoints an denen der Advice ausgeführt wird ist der Pointcut zuständig. In Spring gibt es folgende Arten von Advices, die bestimmen wie der Advice ausgeführt wird:
before Advice wird vor der Methode ausgeführt.
after returning Wird ausgeführt wenn die Methode normal (ohne Exception) beendet.
after throwing Wird ausgeführt wenn die Methode mit dem Werfen einer Exception beendet.
after finally Wird nach Beendigung der Methode ausgeführt, egal ob normal oder mit Exception.
around Wird vor und nach der Methode ausgeführt. Es gibt die Möglichkeit auf das Ergebnis der Methode zu reagieren.
Spring benutzt AOP via Proxy. Das heißt Spring erstellt um das mit einem Aspekt zu versehende Objekt einen Proxy:

Ohne AOP (kein Proxy): der Aufruf wird direkt auf dem Objekt ausgeführt.

Mit AOP (mit Proxy): In diesem Fall ein before-Advice. Der Aufruf wird von dem Proxy abgefangen, der Advice ausgeführt und danach die original-Methode des Objekts aufgerufen.

Das Versehen eines Objekts mit einem Proxy heisst Weaving und wird von Spring zur Laufzeit ausgeführt. Dieses Vorgehen ist bei final Klassen nicht möglich, da der Proxy die Klasse in den per Advice zu erreichenden Methoden erweitert. Spring bietet drei Möglichkeiten Aspektorientierung zu implementieren, die im folgenden vorgestellt werden.

@AspectJ in Spring

Spring bietet die Möglichkeit Aspekte, Advices und Pointcut in der @AspectJ-Syntax zu definieren. Die so programmierten Aspekte werden von Spring durchsucht und die nötigen Proxies automatisch erzeugt. AspectJ in Spring ist dabei nicht so umfangreich wie AspectJ selbst, dafür aber kleiner und in Spring integriert. Die erzeugten Aspekte können, falls das gesamte AspectJ nötig wird, weiterverwendet werden, sie sind "Upgradbar". Damit Spring weiss, dass Aspektorientierte Programmierung mit @AspectJ-Syntax verwendet werdet wird, ist es nötig den XML-Header anzupassen, sowie das zusätzliche Tag <aop:aspectj-autoproxy/> einzuführen. Der Aspekt selbst wird ebenfalls als Bean bekanntgemacht:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
  <aop:aspectj-autoproxy/>
  <bean id="loggingAspect" class="Logging">
    <!-- ggf. properties / constructor-args -->
  </bean>
  <!-- andere Beans -->
</beans>
Die Änderungen im Header machen den AOP-Namespace bekannt, damit <aop:aspectj-autoproxy/> verstanden wird. In der Klasse Logging ist der Aspekt mit Pointcuts und Advices definiert:
import org.aspectj.lang.annotations.Aspect;
import org.aspectj.lang.annotations.Pointcut;
import org.aspectj.lang.annotations.Before;
import org.aspectj.lang.annotations.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class Logging {

  @Pointcut("execution(* myTask.do(..))")
  public void doWork() {}

  @Before("doWork()") 
  public void doLogging() {
    //Aktionen vor der myTask.do()-Methode
  }

  @Before("execution(* myTask.do(..))")
  public void doLoggingToo() {
    //Gleichwertig mit dem Before(doWork())-Advice
  }

  @Around("doWork()") 
  public Object doLoggingAround(ProceedingJoinPoint pjp) {
    //Aktionen vor der Ausführung von myTask.do()
    Object retVal = pjp.proceed(); //myTask.do() ausführen
    //Aktionen nach der Ausführung von myTask.do()
    return retval;
  }
}
In der Klasse gibt @Aspect an, dass es sich bei dieser Klasse um einen Aspekt handelt. @Pointcut gibt ein Muster für die Stellen an, an denen sich darauf beziehenden Advices ausgeführt werden. Die Muster des Pointcuts lassen dabei Verknüpfungen wie || (oder) oder && (und) zu. Die Syntax der Pointcuts bei @AspectJ in Spring und AOP mit XML-Schema lässt Wildcards zu. So bedeutet "(* myTask.do(..))", dass der Pointcut für die Methode do aus der Klasse myTask unabhängig von den angegebenen Parametern gilt. Der erste @Before-Advice bezieht sich auf diesen Pointcut und die Methode doLogging wird vor der Methode do ausgeführt. Der zweite @Before-Advice erzeugt sich seinen eigenen Pointcut. Dies ist sinnvoll wenn an diesem Pointcut nur ein Advice ausgeführt werden soll. Der @Around-Advice zeigt wie die Aktionen vor- und hinter der do-Methode ausgeführt werden.

AOP per XML-Schema

Die zweite Möglichkeit Aspektorientierte Programmierung in Spring zu ermöglichen ist, die Aspekte, Pointcuts und Advices in der XML-Konfigurationsdatei festzulegen. Dies hat den Vorteil, dass auch die Aspekte an einer zentralen Stelle verwaltet werden, bringt allerdings den Nachteil mit sich, dass man in der Klasse nicht mehr erkennen kann, dass es sich um einen Aspekt handelt. Außerdem gibt es bei dieser Konfiguration nicht die Möglichkeit Pointcuts mit || oder && zu verknüpfen. Der Header der XML-Datei entspricht dabei dem der Benutzung der @AspectJ-Syntax. Eine mit dem obigen Beispiel gleichwertige Konfiguration würde folgendermaßen aussehen:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
  <bean id="loggingAspect" class="Logging">
    <!-- ggf. properties / constructor-args -->
  </bean>
  <aop:config>
    <aop:aspect ref="loggingAspect">
      <aop:pointcut id="doWork" expression="execution(* myTask.do(..))"/>
      <aop:before pointcut-ref="doWork" method="doLogging"/>
      <aop:before pointcut="execution(* myTask.do(..))" method="doLoggingToo"/>
      <aop:around pointcut-ref="doWork" method="doLoggingAround"/>
    </aop:aspect>
  </aop:config>
  <!-- andere Beans -->
</beans>
Das <aop:config>-Tag leitet die Konfiguration ein. In diesem Tag können mehrere Aspekte konfiguriert werden. Es ist auch möglich Pointcuts direkt unter diesem Tag zu konfigurieren, wodurch sie in allen Aspekten genutzt werden können. Die Konfiguration von Pointcuts erfolgt im <aop:pointcut>-Tag mit einer id zum referenzieren und einer expression, die das Muster vorgibt an welchen Stellen ein Advice ausgeführt werden soll. Die Tags für die unterschiedlichen Adivces lauten <aop:before>, <aop:after-returning>, <aop:after-throwing>, <aop:after-finally> sowie <aop:around>. Die Attribute sind für alle Advices pointcut-ref oder pointcut, wodurch Pointcuts referenziert oder angegeben werden, sowie method, wo die auszuführende Methode angegeben wird.

AOP mit Spring API

Die letzte Möglichkeit AOP mit Spring zu realisieren ist die Nutzung einer von Spring zur Verfügung gestellen API. Für Pointcuts bietet Spring ein Interface gegen das selber Pointcuts programmiert werden könnne oder vorgefertige Klassen für Regular Expressions, die nur noch in der XML-Konfigurationsdatei über ihre Properties konfiguriert werden müssen. Die Advices werden gegen Interfaces programmiert. Für jeden möglichen Advice-Typ liefert Spring Interfaces mit. Wenn ein Aspekt nur einen Advice und einen Pointcut umfasst können diese zu einem Advisor zusammengefasst werden. Die Proxies für die Objekte auf denen die Advices ausgeführt werden sollen, müssen in der XML-Datein konfiguriert werden. Ist für einen Advice kein Pointcut vorhanden und nur der Proxy definiert, werden die Advices auf alle Methodenaufrufe in diesem Proxy ausgeführt. Diese Möglichkeit der Aspektorientierten Programmierung wurde nur implementiert um Abwärtskompatiblität zu Spring 1.0 zu gewährleisten. Da die Advices und eventuelle selbstgeschriebene Pointcuts hier von den Spring-Interfaces abhängen sollte sie nicht genutzt werden.
... [ Seminar BS, WWW und PS ] ... [ Thema Spring Framework ] ... [ Features: Aspektorientierte Programmierung ] ... [ Features: Integration anderer Libraries und Frameworks ] ...