Einführung in AspectJ

1. Einleitung

Dieser Artikel ist eine schnelle und praktische Einführung in AspectJ.

Zuerst zeigen wir, wie die aspektorientierte Programmierung aktiviert wird, und dann konzentrieren wir uns auf den Unterschied zwischen Weben während der Kompilierung, nach der Kompilierung und beim Laden.

Beginnen wir mit einer kurzen Einführung in die aspektorientierte Programmierung (AOP) und die Grundlagen von AspectJ.

2. Übersicht

AOP ist ein Programmierparadigma, das darauf abzielt, die Modularität zu erhöhen, indem Querschnittsthemen getrennt werden. Dazu wird dem vorhandenen Code zusätzliches Verhalten hinzugefügt, ohne dass der Code selbst geändert wird. Stattdessen deklarieren wir separat, welcher Code geändert werden soll.

AspectJ implementiert sowohl Bedenken als auch das Verweben von Querschnittsthemen mithilfe von Erweiterungen der Java-Programmiersprache.

3. Maven-Abhängigkeiten

AspectJ bietet je nach Verwendung unterschiedliche Bibliotheken an. Wir finden Maven-Abhängigkeiten unter der Gruppe org.aspectj im Maven Central-Repository.

In diesem Artikel konzentrieren wir uns auf Abhängigkeiten, die zum Erstellen von Aspekten und Weaver mithilfe der Weaver zur Kompilierungszeit, Nachkompilierung und Ladezeit erforderlich sind.

3.1. AspectJ Runtime

Wenn Sie ein AspectJ-Programm ausführen, sollte der Klassenpfad die Klassen und Aspekte zusammen mit der AspectJ-Laufzeitbibliothek aspectjrt.jar enthalten :

 org.aspectj aspectjrt 1.8.9 

Diese Abhängigkeit ist über Maven Central verfügbar.

3.2. AspectJWeaver

Neben der AspectJ-Laufzeitabhängigkeit müssen wir auch die aspectjweaver.jar einschließen , um Ratschläge zur Java-Klasse beim Laden einzuführen:

 org.aspectj aspectjweaver 1.8.9 

Die Abhängigkeit ist auch in Maven Central verfügbar.

4. Aspekterstellung

AspectJ bietet eine Implementierung von AOP und hat drei Kernkonzepte:

  • Join Point
  • Pointcut
  • Rat

Wir werden diese Konzepte demonstrieren, indem wir ein einfaches Programm zur Validierung eines Benutzerkontostands erstellen.

Erstellen wir zunächst eine Kontoklasse mit einem bestimmten Kontostand und einer Methode zum Abheben:

public class Account { int balance = 20; public boolean withdraw(int amount) { if (balance < amount) { return false; } balance = balance - amount; return true; } }

Wir erstellen eine AccountAspect.aj- Datei, um Kontoinformationen zu protokollieren und den Kontostand zu überprüfen (beachten Sie, dass AspectJ-Dateien mit der Dateierweiterung " .aj " enden ):

public aspect AccountAspect { final int MIN_BALANCE = 10; pointcut callWithDraw(int amount, Account acc) : call(boolean Account.withdraw(int)) && args(amount) && target(acc); before(int amount, Account acc) : callWithDraw(amount, acc) { } boolean around(int amount, Account acc) : callWithDraw(amount, acc) { if (acc.balance < amount) { return false; } return proceed(amount, acc); } after(int amount, Account balance) : callWithDraw(amount, balance) { } }

Wie wir sehen können, haben wir der Rückzugsmethode einen Pointcut hinzugefügt und drei Hinweise erstellt , die sich auf den definierten Pointcut beziehen .

Um Folgendes zu verstehen, führen wir die folgenden Definitionen ein:

  • Aspekt : Eine Modularisierung eines Unternehmens, das mehrere Objekte umfasst. Jeder Aspekt konzentriert sich auf eine bestimmte Querschnittsfunktion
  • Verbindungspunkt : Ein Punkt während der Ausführung eines Skripts, z. B. die Ausführung einer Methode oder der Zugriff auf Eigenschaften
  • Hinweis : Maßnahmen, die von einem Aspekt an einem bestimmten Verbindungspunkt ergriffen werden
  • Pointcut : Ein regulärer Ausdruck, der mit Verknüpfungspunkten übereinstimmt. Ein Hinweis ist einem Pointcut-Ausdruck zugeordnet und wird an jedem Verknüpfungspunkt ausgeführt, der dem Pointcut entspricht

Weitere Informationen zu diesen Konzepten und ihrer spezifischen Semantik finden Sie unter folgendem Link.

Als nächstes müssen wir die Aspekte in unseren Code einbinden. In den folgenden Abschnitten werden drei verschiedene Arten des Webens behandelt: Weben während der Kompilierung, Weben nach dem Kompilieren und Weben während der Ladezeit in AspectJ.

5. Weben zur Kompilierungszeit

Der einfachste Ansatz beim Weben ist das Weben zur Kompilierungszeit. Wenn wir sowohl den Quellcode des Aspekts als auch den Code haben, in dem wir Aspekte verwenden, kompiliert der AspectJ-Compiler aus dem Quellcode und erstellt als Ausgabe gewebte Klassendateien. Anschließend wird bei der Ausführung Ihres Codes die Ausgabeklasse des Webprozesses als normale Java-Klasse in JVM geladen.

Wir können die AspectJ-Entwicklungstools herunterladen, da sie einen mitgelieferten AspectJ-Compiler enthalten. Eine der wichtigsten Funktionen von AJDT ist ein Tool zur Visualisierung von Querschnittsthemen, das beim Debuggen einer Pointcut-Spezifikation hilfreich ist. Wir können den kombinierten Effekt bereits vor der Bereitstellung des Codes visualisieren.

Wir verwenden das AspectJ Maven Plugin von Mojo, um AspectJ-Aspekte mithilfe des AspectJ-Compilers in unsere Klassen einzubinden.

 org.codehaus.mojo aspectj-maven-plugin 1.7  1.8 1.8 1.8 true true ignore UTF-8       compile  test-compile    

Weitere Informationen zur Optionsreferenz des AspectJ-Compilers finden Sie unter folgendem Link.

Fügen wir einige Testfälle für unsere Account-Klasse hinzu:

public class AccountTest { private Account account; @Before public void before() { account = new Account(); } @Test public void given20AndMin10_whenWithdraw5_thenSuccess() { assertTrue(account.withdraw(5)); } @Test public void given20AndMin10_whenWithdraw100_thenFail() { assertFalse(account.withdraw(100)); } }

Wenn wir die Testfälle ausführen, bedeutet der folgende Text, der in der Konsole angezeigt wird, dass wir den Quellcode erfolgreich gewebt haben:

[INFO] Join point 'method-call (boolean com.baeldung.aspectj.Account.withdraw(int))' in Type 'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20) advised by around advice from 'com.baeldung.aspectj.AccountAspect' (AccountAspect.class:18(from AccountAspect.aj)) [INFO] Join point 'method-call (boolean com.baeldung.aspectj.Account.withdraw(int))' in Type 'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20) advised by before advice from 'com.baeldung.aspectj.AccountAspect' (AccountAspect.class:13(from AccountAspect.aj)) [INFO] Join point 'method-call (boolean com.baeldung.aspectj.Account.withdraw(int))' in Type 'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20) advised by after advice from 'com.baeldung.aspectj.AccountAspect' (AccountAspect.class:26(from AccountAspect.aj)) 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.AccountAspect - Balance before withdrawal: 20 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.AccountAspect - Withdraw ammout: 5 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.AccountAspect - Balance after withdrawal : 15 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.AccountAspect - Balance before withdrawal: 20 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.AccountAspect - Withdraw ammout: 100 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.AccountAspect - Withdrawal Rejected! 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.AccountAspect - Balance after withdrawal : 20

6. Weben nach dem Kompilieren

Das Weben nach dem Kompilieren (manchmal auch als binäres Weben bezeichnet) wird zum Weben vorhandener Klassendateien und JAR-Dateien verwendet. Wie beim Weben zur Kompilierungszeit können die zum Weben verwendeten Aspekte in Quell- oder Binärform vorliegen und selbst durch Aspekte gewebt werden.

Um dies mit dem AspectJ Maven Plugin von Mojo zu tun, müssen wir alle JAR-Dateien einrichten, die wir in der Plugin-Konfiguration weben möchten:

   org.agroup to-weave   org.anothergroup gen   

The JAR files containing the classes to weave must be listed as in the Maven project and listed as in the of the AspectJ Maven Plugin.

7. Load-Time Weaving

Load-time weaving is simply binary weaving deferred until the point that a class loader loads a class file and defines the class to the JVM.

To support this, one or more “weaving class loaders” are required. These are either provided explicitly by the run-time environment or enabled using a “weaving agent”.

7.1. Enabling Load-Time Weaving

AspectJ load-time weaving can be enabled using AspectJ agent that can get involved in the class loading process and weave any types before they are defined in the VM. We specify the javaagent option to the JVM -javaagent:pathto/aspectjweaver.jar or using Maven plugin to configure the javaagent :

 org.apache.maven.plugins maven-surefire-plugin 2.10   -javaagent:"${settings.localRepository}"/org/aspectj/ aspectjweaver/${aspectj.version}/ aspectjweaver-${aspectj.version}.jar  true always  

7.2. Configuration Weaver

AspectJ's load-time weaving agent is configured by the use of aop.xml files. It looks for one or more aop.xml files on the classpath in the META-INF directory and aggregates the contents to determine the weaver configuration.

An aop.xml file contains two key sections:

  • Aspects: defines one or more aspects to the weaver and controls which aspects are to be used in the weaving process. The aspects element may optionally contain one or more include and exclude elements (by default, all defined aspects are used for weaving)
  • Weaver: defines weaver options to the weaver and specifies the set of types that should be woven. If no include elements are specified then all types visible to the weaver will be woven

Let's configure an aspect to the weaver:

As we can see, we have configured an aspect that points to the AccountAspect, and only the source code in the com.baeldung.aspectj package will be woven by AspectJ.

8. Annotating Aspects

In addition to the familiar AspectJ code-based style of aspect declaration, AspectJ 5 also supports an annotation-based style of aspect declaration. We informally call the set of annotations that support this development style the “@AspectJ” annotations.

Let's create an annotation:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Secured { public boolean isLocked() default false; }

We use the @Secured annotation to enable or disable a method:

public class SecuredMethod { @Secured(isLocked = true) public void lockedMethod() { } @Secured(isLocked = false) public void unlockedMethod() { } }

Next, we add an aspect using AspectJ annotation-style, and check the permission based on the attribute of the @Secured annotation:

@Aspect public class SecuredMethodAspect { @Pointcut("@annotation(secured)") public void callAt(Secured secured) { } @Around("callAt(secured)") public Object around(ProceedingJoinPoint pjp, Secured secured) throws Throwable { return secured.isLocked() ? null : pjp.proceed(); } }

For more detail on AspectJ annotation-style, we can check out the following link.

Next, we weave our class and aspect using load-time weaver and put aop.xml under META-INF folder:

Finally, we add unit test and check the result:

@Test public void testMethod() throws Exception { SecuredMethod service = new SecuredMethod(); service.unlockedMethod(); service.lockedMethod(); }

Wenn wir die Testfälle ausführen, überprüfen wir möglicherweise die Konsolenausgabe, um sicherzustellen, dass wir unseren Aspekt und unsere Klasse im Quellcode erfolgreich verwoben haben:

[INFO] Join point 'method-call (void com.baeldung.aspectj.SecuredMethod.unlockedMethod())' in Type 'com.baeldung.aspectj.test.SecuredMethodTest' (SecuredMethodTest.java:11) advised by around advice from 'com.baeldung.aspectj.SecuredMethodAspect' (SecuredMethodAspect.class(from SecuredMethodAspect.java)) 2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.SecuredMethod - unlockedMethod 2016-11-15 22:53:51 [main] INFO c.b.aspectj.SecuredMethodAspect - public void com.baeldung.aspectj.SecuredMethod.lockedMethod() is locked

9. Fazit

In diesem Artikel haben wir einführende Konzepte zu AspectJ behandelt. Weitere Informationen finden Sie auf der AspectJ-Homepage.

Den Quellcode für diesen Artikel finden Sie auf GitHub.