Introducción a AspectJ

1. Introducción

Este artículo es una introducción rápida y práctica a AspectJ.

Primero, mostraremos cómo habilitar la programación orientada a aspectos, y luego nos centraremos en la diferencia entre el tejido en tiempo de compilación, postcompilación y tiempo de carga.

Comencemos con una breve introducción a la programación orientada a aspectos (AOP) y los conceptos básicos de AspectJ.

2. Resumen

AOP es un paradigma de programación que tiene como objetivo aumentar la modularidad al permitir la separación de preocupaciones transversales. Lo hace agregando comportamiento adicional al código existente sin modificar el código en sí. En cambio, declaramos por separado qué código se va a modificar.

AspectJ implementa tanto las preocupaciones como el tejido de preocupaciones transversales utilizando extensiones del lenguaje de programación Java.

3. Dependencias de Maven

AspectJ ofrece diferentes bibliotecas según su uso. Podemos encontrar dependencias de Maven en el grupo org.aspectj en el repositorio de Maven Central.

En este artículo, nos enfocamos en las dependencias necesarias para crear aspectos y Weaver usando Weavers en tiempo de compilación, postcompilación y tiempo de carga.

3.1. Tiempo de ejecución de AspectJ

Cuando se ejecuta un programa de AspectJ, la ruta de clase debe contener las clases y aspectos, junto con la biblioteca de ejecución AspectJ aspectjrt.jar :

 org.aspectj aspectjrt 1.8.9 

Esta dependencia está disponible en Maven Central.

3.2. AspectJWeaver

Además de la dependencia del tiempo de ejecución de AspectJ, también necesitaremos incluir el aspecto del archivo .jar para introducir consejos en la clase Java en el momento de la carga:

 org.aspectj aspectjweaver 1.8.9 

La dependencia también está disponible en Maven Central.

4. Creación de aspectos

AspectJ proporciona una implementación de AOP y tiene tres conceptos básicos:

  • Punto de unión
  • Pointcut
  • Consejo

Demostraremos estos conceptos creando un programa simple para validar el saldo de una cuenta de usuario.

Primero, creemos una clase de Cuenta con un saldo dado y un método para retirar:

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

Crearemos un archivo AccountAspect.aj para registrar la información de la cuenta y validar el saldo de la cuenta (tenga en cuenta que los archivos AspectJ terminan con una extensión de archivo " .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) { } }

Como podemos ver, hemos añadido un punto de corte con el método de retirarse y creó tres aconseja que se refieren a la definida punto de corte .

Para comprender lo siguiente, presentamos las siguientes definiciones:

  • Aspecto : modularización de una preocupación que atraviesa varios objetos. Cada aspecto se centra en una funcionalidad transversal específica
  • Punto de unión : un punto durante la ejecución de un script, como la ejecución de un método o el acceso a una propiedad.
  • Consejo : acción realizada por un aspecto en un punto de unión en particular
  • Pointcut : una expresión regular que coincide con los puntos de unión. Un consejo está asociado con una expresión de corte puntual y se ejecuta en cualquier punto de unión que coincida con el corte puntual

Para obtener más detalles sobre estos conceptos y su semántica específica, es posible que deseemos consultar el siguiente enlace.

A continuación, debemos tejer los aspectos en nuestro código. Las secciones siguientes abordan tres tipos diferentes de tejido: tejido en tiempo de compilación, tejido después de la compilación y tejido en tiempo de carga en AspectJ.

5. Tejido en tiempo de compilación

El enfoque más simple de tejido es el tejido en tiempo de compilación. Cuando tengamos tanto el código fuente del aspecto como el código en el que estamos usando aspectos, el compilador de AspectJ compilará desde la fuente y producirá archivos de clase tejidos como salida. Posteriormente, tras la ejecución de su código, la clase de salida del proceso de tejido se carga en JVM como una clase Java normal.

Podemos descargar las herramientas de desarrollo de AspectJ, ya que incluye un compilador de AspectJ incluido. Una de las características más importantes de AJDT es una herramienta para la visualización de inquietudes transversales, que es útil para depurar una especificación de corte puntual. Podemos visualizar el efecto combinado incluso antes de que se implemente el código.

Usamos el complemento AspectJ Maven de Mojo para tejer aspectos de AspectJ en nuestras clases usando el compilador AspectJ.

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

Para obtener más detalles sobre la referencia de opciones del compilador AspectJ, es posible que deseemos consultar el siguiente enlace.

Agreguemos algunos casos de prueba para nuestra clase de cuenta:

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

Cuando ejecutamos los casos de prueba, el texto a continuación que se muestra en la consola significa que hemos tejido con éxito el código fuente:

[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. Tejido posterior a la compilación

El tejido posterior a la compilación (también llamado a veces tejido binario) se utiliza para tejer archivos de clases y archivos JAR existentes. Al igual que con el tejido en tiempo de compilación, los aspectos utilizados para el tejido pueden estar en forma de fuente o binaria, y ellos mismos pueden estar tejidos por aspectos.

Para hacer esto con el complemento AspectJ Maven de Mojo, necesitamos configurar todos los archivos JAR que nos gustaría tejer en la configuración del complemento:

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

Cuando ejecutamos los casos de prueba, podemos verificar la salida de la consola para verificar que entrelazamos exitosamente nuestro aspecto y clase en el código fuente:

[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. Conclusión

En este artículo, cubrimos conceptos introductorios sobre AspectJ. Para más detalles, puede echar un vistazo a la página de inicio de AspectJ.

Puede encontrar el código fuente de este artículo en GitHub.