✂️ AspectJ Cheatsheet Completo ✂️

AspectJ es una extensión “seamless” (sin costuras) de Java que implementa la Programación Orientada a Aspectos (AOP). Permite la modularización de preocupaciones transversales (cross-cutting concerns) que no encajan bien en la programación orientada a objetos tradicional, mejorando la modularidad, mantenibilidad y reusabilidad del código.


1. 🌟 Conceptos Clave de AOP y AspectJ


2. 🛠️ Configuración Inicial (Maven con Compile-Time Weaving)

Compile-time weaving es la forma más potente de AspectJ, ya que modifica directamente el bytecode de las clases para inyectar el aspecto.

  1. Añadir dependencias en pom.xml:
    <dependencies>
        &lt;!-- AspectJ Runtime (para que el código compilado pueda ejecutar el aspecto) -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.21</version> &lt;!-- Usar la versión más reciente -->
        </dependency>
        &lt;!-- AspectJ Weaver (necesario para compilar el código con AspectJ) -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.21</version>
            <scope>runtime</scope> &lt;!-- Solo necesario en tiempo de ejecución si usas LTW -->
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            &lt;!-- Plugin para configurar el compilador Java -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
            &lt;!-- AspectJ Maven Plugin para Compile-Time Weaving -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.14.0</version>
                <configuration>
                    <complianceLevel>17</complianceLevel> &lt;!-- Tu versión de Java -->
                    <Xlint>warning</Xlint>
                    <showWeaveInfo>true</showWeaveInfo> &lt;!-- Muestra información de weaving -->
                    <verbose>true</verbose>
                    <aspectLibraries>
                        &lt;!-- Si tuvieras aspectos en librerías externas -->
                        &lt;!-- <aspectLibrary>
                            <groupId>com.example</groupId>
                            <artifactId>my-logging-aspect</artifactId>
                        </aspectLibrary> -->
                    </aspectLibraries>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>    &lt;!-- Para compilar AspectJ -->
                            <goal>test-compile</goal> &lt;!-- Para compilar tests con AspectJ -->
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency> &lt;!-- Importante: la misma versión que aspectjweaver -->
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>1.9.21</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

3. 📝 Definición de Aspectos (Estilo AspectJ Native)

Un aspecto es una clase Java especial con la extensión .aj o una clase Java con anotaciones específicas (estilo Spring AOP, pero AspectJ también las soporta).

// src/main/java/com/example/myapp/logging/LoggingAspect.java (O LoggingAspect.aj)
package com.example.myapp.logging;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*; // Anotaciones de AspectJ

// @Aspect (solo si usas el estilo de anotaciones de AspectJ)
public aspect LoggingAspect { // La palabra clave 'aspect' para aspectos nativos

    // 1. Pointcuts (Expresiones para seleccionar Join Points)
    // Se definen con la palabra clave 'pointcut' (o @Pointcut para anotaciones)

    // Un pointcut que selecciona la ejecución de cualquier método público
    // dentro del paquete com.example.myapp.service
    pointcut serviceMethodExecution(): execution(public * com.example.myapp.service.*.*(..));

    // Un pointcut que selecciona cualquier método que termine con 'Controller'
    pointcut controllerMethod(): execution(* *..*Controller.*(..));

    // Un pointcut para un método específico
    pointcut createUserMethod(): execution(* com.example.myapp.service.UserService.createUser(..));

    // Un pointcut que toma un argumento de método
    pointcut methodWithUserId(Long userId): args(userId, ..) && execution(* com.example.myapp.service.UserService.getUserById(..));

    // Combinación de pointcuts
    pointcut allBusinessOperations(): serviceMethodExecution() || controllerMethod();


    // 2. Advice (El código a ejecutar en los Join Points)
    // Se definen con palabras clave (o anotaciones como @Before, @AfterReturning, etc.)

    // Before Advice: Se ejecuta antes de la ejecución del Join Point
    before(): serviceMethodExecution() {
        System.out.println("LOG: Antes de ejecutar un método de servicio.");
    }

    before(JoinPoint jp): allBusinessOperations() { // Acceso a JoinPoint
        System.out.println("LOG: Entrada a " + jp.getSignature().getName() + " en " + jp.getSignature().getDeclaringTypeName());
        // jp.getArgs(): argumentos del método
    }

    // After Advice: Se ejecuta después de la ejecución del Join Point (siempre, haya excepción o no)
    after(): serviceMethodExecution() {
        System.out.println("LOG: Después de ejecutar un método de servicio.");
    }

    // After Returning Advice: Se ejecuta después de la ejecución exitosa del Join Point (si devuelve un valor)
    afterReturning(Object result): serviceMethodExecution() { // result es el valor devuelto
        System.out.println("LOG: Retornando: " + result);
    }

    // After Throwing Advice: Se ejecuta si el Join Point lanza una excepción
    afterThrowing(Throwable ex): serviceMethodExecution() { // ex es la excepción lanzada
        System.err.println("LOG: Excepción lanzada: " + ex.getClass().getSimpleName() + " - " + ex.getMessage());
    }

    // Around Advice: Envuelve la ejecución del Join Point. Puede modificar argumentos, resultado o la ejecución misma.
    Object around(): serviceMethodExecution() {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            // Proceed: Invoca el Join Point original
            result = proceed(); // O proceed(argumentos_modificados)
            long endTime = System.currentTimeMillis();
            System.out.println("LOG: " + thisJoinPointStaticPart.getSignature().getName() + " ejecutado en " + (endTime - startTime) + "ms. Resultado: " + result);
            return result;
        } catch (Throwable e) {
            long endTime = System.currentTimeMillis();
            System.err.println("LOG: " + thisJoinPointStaticPart.getSignature().getName() + " falló en " + (endTime - startTime) + "ms. Error: " + e.getMessage());
            throw e; // Re-lanza la excepción
        }
    }

    // 3. Introducción (Inter-type Declaration): Añadir métodos/campos a clases existentes
    // Requiere la sintaxis .aj o LTW con anotaciones.
    // public void com.example.myapp.service.UserService.newMethod() {
    //     System.out.println("Este método fue introducido por un aspecto.");
    // }

    // Introducción de interfaz
    // declare parents: com.example.myapp.service.UserService implements com.example.myapp.security.Securable;
}

3.1. Aspectos Basados en Anotaciones (Estilo Spring AOP, soportado por AspectJ)

Si estás trabajando en un entorno Spring Boot, a menudo usarás Spring AOP, que se basa en proxies y solo soporta execution Join Points en métodos públicos. Sin embargo, AspectJ puede interpretar estas anotaciones y hacer Compile-Time Weaving para un AOP más completo.

// src/main/java/com/example/myapp/logging/AnnotatedLoggingAspect.java
package com.example.myapp.logging;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component; // Para Spring IoC

@Aspect // Marca la clase como un aspecto
@Component // Para que Spring la gestione como un bean
public class AnnotatedLoggingAspect {

    // Pointcut para todos los métodos en el paquete de servicio
    @Pointcut("execution(* com.example.myapp.service.*.*(..))")
    private void serviceMethods() {}

    // Around Advice para medir el tiempo de ejecución
    @Around("serviceMethods()")
    public Object profileServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed(); // Invoca el método original
        long end = System.currentTimeMillis();
        System.out.println("Annotated LOG: " + joinPoint.getSignature().getName() +
                           " took " + (end - start) + "ms and returned " + result);
        return result;
    }

    // Puedes añadir @Before, @AfterReturning, @AfterThrowing aquí también
}

4. 🌐 Pointcut Expressions (Expresiones de Punto de Corte)

La sintaxis para seleccionar Join Points.


5. ⚙️ Tipos de Advice (Consejos)


6. 🌐 Load-Time Weaving (LTW)

Alternativa a Compile-Time Weaving, útil cuando no puedes modificar el proceso de compilación (ej. librerías de terceros).

  1. Dependencias: aspectjweaver con scope=runtime.
  2. META-INF/aop.xml: Archivo de configuración que especifica qué aspectos tejer.
    &lt;!-- src/main/resources/META-INF/aop.xml -->
    <aspectj>
        <aspects>
            &lt;!-- Define tu aspecto aquí -->
            <aspect name="com.example.myapp.logging.LoggingAspect"/>
            &lt;!-- O si es un aspecto basado en anotaciones -->
            &lt;!-- <aspect name="com.example.myapp.logging.AnnotatedLoggingAspect"/> -->
        </aspects>
        <weaver options="-verbose">
            &lt;!-- Incluir/excluir clases para tejer -->
            <include within="com.example.myapp..*"/>
        </weaver>
    </aspectj>
  3. Configurar Agente Java: Ejecutar la JVM con el agente aspectjweaver.
    java -javaagent:/path/to/aspectjweaver-1.9.21.jar -jar your_app.jar
    • Para Spring Boot, puedes añadir la dependencia spring-aspects y usar --spring.profiles.active=aspectj o configurar explícitamente el agente.

7. 💡 Buenas Prácticas y Consejos


Este cheatsheet te proporciona una referencia completa de AspectJ, cubriendo sus conceptos esenciales de AOP, cómo configurar el weaving, definir aspectos y consejos, usar expresiones de pointcut y aplicar las mejores prácticas para modularizar tus preocupaciones transversales de manera efectiva en aplicaciones Java.