CodeGym /Cursos /Módulo 5. Spring /Cómo funciona AOP en Spring: anotaciones @Aspect, @Around...

Cómo funciona AOP en Spring: anotaciones @Aspect, @Around, @Before, @After

Módulo 5. Spring
Nivel 3 , Lección 3
Disponible

La anotación @Aspect: la puerta de entrada al mundo de AOP

Para empezar con AOP en Spring, hay que definir un aspecto. Se usa la anotación @Aspect, que le indica al contenedor de Spring que la clase es un aspecto. Imagina esto como una señal: "¡Eh, Spring, esta clase tiene lógica transversal, por favor vigílala!".

Ejemplo: crear un aspecto Supongamos que tenemos un servicio que ejecuta operaciones de negocio y queremos loguear las llamadas a todos sus métodos:


import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component // Registro del aspecto como Spring Bean
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBeforeMethodCall() {
        System.out.println("¡Se ha llamado al método!");
    }
}

¿Qué pasa aquí?

  1. @Aspect: convierte esta clase en un aspecto.
  2. @Component: permite que Spring registre el aspecto como bean (sin esto, la anotación @Aspect no funcionará).
  3. @Before: marca el método que debe ejecutarse antes del pointcut indicado.

Dato curioso: AOP usa magia de objetos proxy! Pero no te preocupes, pronto veremos cómo funciona eso entre bastidores.


Particularidades al usar la anotación @Around

La anotación @Around es el todoterreno de AOP. Permite ejecutar lógica tanto antes como después del método. Además, con ella puedes controlar la invocación del método o incluso modificar su resultado.

Ejemplo: medir el tiempo de ejecución de métodos

Supongamos que queremos medir cuánto tarda la llamada de cada método en el servicio:


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PerformanceAspect {

    @Around("execution(* com.example.service.*.*(..))")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result = joinPoint.proceed(); // Ejecutamos el método objetivo

        long endTime = System.currentTimeMillis();
        System.out.println("El método " + joinPoint.getSignature() + " se ejecutó durante "
                           + (endTime - startTime) + " ms");
        return result; // Devolvemos el resultado de la llamada al método
    }
}

¿Qué pasa aquí?

  1. ProceedingJoinPoint: representa el método al que está ligado el aspecto. Con él podemos llamar al método o incluso omitirlo.
  2. proceed(): ejecuta el método objetivo.
  3. Medición de tiempo: registramos la diferencia de tiempo antes y después de la llamada al método.

Al usar @Around, recuerda que la llamada a proceed() es obligatoria: sin ella el método objetivo simplemente no se ejecutará.

Uso de las anotaciones @Before y @After

Si quieres añadir lógica solo ANTES o DESPUÉS de la ejecución de un método, las anotaciones @Before y @After te ayudan. Veamos ejemplos concretos.

Ejemplo con @Before

Imagina que queremos comprobar si se llama a un método que pertenece a cierta clase:


import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AuthenticationAspect {

    @Before("execution(* com.example.security.*.*(..))")
    public void checkAuthentication() {
        System.out.println("Comprobando la autenticación del usuario...");
    }
}

Ejemplo con @After

Ahora añadimos alguna lógica que se ejecutará DESPUÉS de la llamada al método. Por ejemplo, limpiamos la caché:


import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class CacheAspect {

    @After("execution(* com.example.service.*.*(..))")
    public void clearCache() {
        System.out.println("Limpiando la caché después de ejecutar el método...");
    }
}
  • @Before: se ejecuta antes de la llamada al método. Ideal para validación de entrada, comprobación de autenticación, etc.
  • @After: se ejecuta después de la finalización del método (exitoso o no). Se usa para tareas de "limpieza": cerrar recursos, cambiar estado, etc.

Ejemplos de uso de @Before y @AfterReturning

Con la anotación @AfterReturning puedes obtener el resultado de la ejecución del método y procesarlo:

Ejemplo: modificar el valor devuelto en @AfterReturning Supongamos que queremos asegurarnos de que todas las cadenas devueltas por un método estén en mayúsculas:


import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ResultAspect {

    @AfterReturning(pointcut = "execution(String com.example.service.*.*(..))",
                    returning = "result")
    public void modifyReturnValue(String result) {
        System.out.println("Resultado devuelto: " + result.toUpperCase());
    }
}

Cómo configurar Pointcut para mayor flexibilidad

La mayoría de las anotaciones en AOP dependen de expresiones Pointcut. Esas expresiones son el corazón de AOP, porque indican a qué join point hay que engancharse. Aquí tienes ejemplos para hacerlo más claro:

Patrones para Pointcut:

  1. execution(* com.example.service.*.*(..)):
    • Todos los métodos en los paquetes com.example.service.
  2. execution(public * *(..)):
    • Todos los métodos public en todas las clases.
  3. execution(* com.example..*.*(..)):
    • Cualquier método en cualquier clase ubicada bajo el paquete com.example.
  4. @annotation(org.springframework.transaction.annotation.Transactional):
    • Aplicar el aspecto a todos los métodos anotados con @Transactional.

Limitaciones y errores típicos

  1. No funciona sin Spring AOP: asegúrate de que el aspecto esté habilitado en la configuración (por ejemplo, con @EnableAspectJAutoProxy en la clase de configuración).
  2. Errores en la expresión Pointcut: si los join points no coinciden con la definición, el aspecto no se ejecutará.
  3. Problemas de rendimiento: usar AOP puede ralentizar la ejecución de un método (especialmente con logging intensivo).
  4. Objetos proxy: recuerda que AOP funciona solo con proxy (se necesitan beans de Spring).

¿Y ahora qué?

Ahora sabes cómo con las anotaciones @Aspect, @Around, @Before y @After crear aspectos potentes y flexibles en Spring. Estas herramientas te ayudan a añadir lógica cross-cutting a la aplicación, manteniendo el código más limpio y modular.

Los siguientes pasos son poner esto en práctica: configurar expresiones Pointcut más complejas y crear aspectos personalizados.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION