Introduzione ad AspectJ

1. Introduzione

Questo articolo è un'introduzione rapida e pratica ad AspectJ.

Per prima cosa, mostreremo come abilitare la programmazione orientata agli aspetti, quindi ci concentreremo sulla differenza tra la tessitura in fase di compilazione, post-compilazione e in fase di caricamento.

Cominciamo con una breve introduzione alla programmazione orientata agli aspetti (AOP) e alle basi di AspectJ.

2. Panoramica

AOP è un paradigma di programmazione che mira ad aumentare la modularità consentendo la separazione delle preoccupazioni trasversali. Lo fa aggiungendo un comportamento aggiuntivo al codice esistente senza modificare il codice stesso. Invece, dichiariamo separatamente quale codice modificare.

AspectJ implementa sia le preoccupazioni che la tessitura di preoccupazioni trasversali utilizzando estensioni del linguaggio di programmazione Java.

3. Dipendenze di Maven

AspectJ offre diverse librerie a seconda del suo utilizzo. Possiamo trovare le dipendenze Maven nel gruppo org.aspectj nel repository Maven Central.

In questo articolo, ci concentriamo sulle dipendenze necessarie per creare aspetti e Weaver utilizzando i Weaver in fase di compilazione, post-compilazione e caricamento.

3.1. AspectJ Runtime

Quando si esegue un programma AspectJ, il classpath dovrebbe contenere le classi e gli aspetti insieme alla libreria di runtime AspectJ aspectjrt.jar :

 org.aspectj aspectjrt 1.8.9 

Questa dipendenza è disponibile su Maven Central.

3.2. AspectJWeaver

Oltre alla dipendenza dal runtime di AspectJ, dovremo includere anche aspectjweaver.jar per introdurre consigli alla classe Java al momento del caricamento:

 org.aspectj aspectjweaver 1.8.9 

La dipendenza è disponibile anche su Maven Central.

4. Creazione di aspetti

AspectJ fornisce un'implementazione di AOP e ha tre concetti fondamentali:

  • Join Point
  • Pointcut
  • Consigli

Dimostreremo questi concetti creando un semplice programma per convalidare il saldo di un account utente.

Per prima cosa, creiamo una classe Account con un dato saldo e un metodo per prelevare:

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

Creeremo un file AccountAspect.aj per registrare le informazioni sull'account e per convalidare il saldo dell'account (si noti che i file AspectJ terminano con l' estensione " .aj "):

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) { } }

Come possiamo vedere, abbiamo aggiunto un pointcut al metododraw e abbiamo creato tre consigli che si riferiscono al pointcut definito .

Per comprendere quanto segue, introduciamo le seguenti definizioni:

  • Aspetto : una modularizzazione di una preoccupazione che attraversa più oggetti. Ogni aspetto si concentra su una specifica funzionalità trasversale
  • Punto di unione : un punto durante l'esecuzione di uno script, come l'esecuzione di un metodo o l'accesso a una proprietà
  • Consiglio : azione intrapresa da un aspetto in un particolare punto di unione
  • Pointcut : un'espressione regolare che corrisponde ai punti di unione. Un consiglio è associato a un'espressione pointcut e viene eseguito in qualsiasi punto di unione che corrisponde al pointcut

Per maggiori dettagli su questi concetti e sulla loro semantica specifica, potremmo voler controllare il seguente collegamento.

Successivamente, dobbiamo intrecciare gli aspetti nel nostro codice. Le sezioni seguenti trattano tre diversi tipi di tessitura: tessitura in fase di compilazione, tessitura post-compilazione e tessitura in fase di caricamento in AspectJ.

5. Tessitura in fase di compilazione

L'approccio più semplice alla tessitura è la tessitura in fase di compilazione. Quando abbiamo sia il codice sorgente dell'aspetto che il codice in cui stiamo usando gli aspetti, il compilatore AspectJ compilerà dal sorgente e produrrà un file di classe intrecciato come output. Successivamente, dopo l'esecuzione del codice, la classe di output del processo di tessitura viene caricata in JVM come una normale classe Java.

Possiamo scaricare gli strumenti di sviluppo AspectJ poiché include un compilatore AspectJ in bundle. Una delle caratteristiche più importanti di AJDT è uno strumento per la visualizzazione di problemi di crosscutting, utile per il debug di una specifica pointcut. Possiamo visualizzare l'effetto combinato anche prima che il codice venga distribuito.

Usiamo il plug-in AspectJ Maven di Mojo per intrecciare aspetti AspectJ nelle nostre classi utilizzando il compilatore AspectJ.

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

Per maggiori dettagli sulle opzioni di riferimento del compilatore AspectJ, potremmo voler controllare il seguente collegamento.

Aggiungiamo alcuni casi di test per la nostra classe Account:

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)); } }

Quando eseguiamo i casi di test, il testo di seguito mostrato nella console significa che abbiamo intrecciato con successo il codice sorgente:

[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. Tessitura post-compilazione

La tessitura post-compilazione (a volte chiamata anche tessitura binaria) viene utilizzata per tessere file di classe e file JAR esistenti. Come per la tessitura in fase di compilazione, gli aspetti utilizzati per la tessitura possono essere in forma sorgente o binaria e possono essere essi stessi tessuti da aspetti.

Per fare ciò con il plug-in AspectJ Maven di Mojo, dobbiamo configurare tutti i file JAR che vorremmo tessere nella configurazione del plug-in:

   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(); }

Quando eseguiamo i casi di test, possiamo controllare l'output della console per verificare di aver intrecciato con successo il nostro aspetto e la nostra classe nel codice sorgente:

[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. Conclusione

In questo articolo, abbiamo trattato i concetti introduttivi su AspectJ. Per i dettagli, puoi dare un'occhiata alla home page di AspectJ.

Puoi trovare il codice sorgente di questo articolo su GitHub.