Spring Boot AOP


Por Martinez Henry


Resumen (TL;DR)

La Programación Orientada a Aspectos (AOP) permite separar funcionalidades transversales del núcleo de negocio. En este artículo aplicamos AOP con Spring Boot para medir el tiempo de ejecución de métodos sin modificar su lógica.


📑 Tabla de Contenido

  1. ¿Qué es AOP?
  2. Entorno
  3. Creando un @Aspect
  4. Creando un @Around
  5. Ejecutando la aplicación

Spring Boot AOP

En esta ocasión aplicaremos el paradigma de Programación Orientada a Aspectos o AOP (Aspect Oriented Programming).

Este paradigma nos permite separar las funcionalidades del sistema, en donde las funcionalidades generales pueden usarse transversalmente y así dejar las funciones modulares en cada uno de los módulos del sistema.

Spring Boot soporta este paradigma a través de la dependencia:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Puedes obtener mayor información de AOP para Spring Boot en Aspect Oriented Programming with Spring.


Entorno

  • Maven
  • Java 11
  • IntelliJ CE

Creando un @Aspect

Para crear un aspecto debemos colocar la anotación @Aspect en la declaración de la clase:

@Aspect
@Service
public class LoggingAspect {

Con esta anotación le indicamos a Spring que esta clase aplicará ejecuciones de aspectos.


Creando un @Around

Para capturar el punto de cruce antes y después de la ejecución de un método, seguimos estos pasos:

Primero creamos el método que deseamos aplicar en nuestro aspecto:

public Object logging(ProceedingJoinPoint joinPoint){
    LOGGER.info("Executing Around method: {}", joinPoint.getSignature().getName());
    long startTime = System.currentTimeMillis();
    Object retVal = joinPoint.proceed();
    long endTime = System.currentTimeMillis();
    long time = endTime - startTime;
    LOGGER.info("Took {} seg.", (time/1000));
    return retVal;
}

Luego agregamos la anotación @Around:

@Around
public Object logging(ProceedingJoinPoint joinPoint){
    ...
}

Por último indicamos el punto de cruce. Usaremos execution(* com.hvs..*(..)) para que se dispare para cualquier ejecución de un método dentro del paquete com.hvs:

@Around("execution(* com.hvs..*(..))")
public Object logging(ProceedingJoinPoint joinPoint){
    LOGGER.info("Executing Around method: {}", joinPoint.getSignature().getName());
    long startTime = System.currentTimeMillis();
    Object retVal = joinPoint.proceed();
    long endTime = System.currentTimeMillis();
    long time = endTime - startTime;
    LOGGER.info("Took {} seg.", (time/1000));
    return retVal;
}

Ejecutando la aplicación

Para validar el aspecto, creamos un controlador que calcula un valor random dentro de un rango recibido como parámetros:

@GetMapping(value = {"/${action-delay}/{min}/{max}"})
public long delay(@PathVariable long min, @PathVariable long max) {
    LOGGER.info("into my method");
    if (min == 0) { min = defaultMin; }
    if (max == 0) { max = defaultMax; }
    if (min < 0) {
        throw new IllegalArgumentException("min value should not be < 0");
    }
    if (max < min) {
        throw new IllegalArgumentException("max value should not be bigger that min value");
    }
    long delay = min + (long) (Math.random() * (max - min));
    sleep((delay * 1000));
    return delay;
}

Al consumir el controlador, en el log veremos:

2021-10-07 14:16:14.533  INFO --- com.hvs.lab.aop.aspect.LoggingAspect     : Executing Around method: delay
2021-10-07 14:16:14.542  INFO --- c.h.l.a.c.i.DelayRandomController        : into my method
2021-10-07 14:16:19.542  INFO --- com.hvs.lab.aop.aspect.LoggingAspect     : Took 5 seg.

Al final de la ejecución vemos Took 5 seg., confirmando que el aspecto se ejecutó correctamente.

Puedes ver el código completo de este ejemplo aquí.


Cierre

AOP es una herramienta poderosa para implementar funcionalidades transversales como logging, métricas, seguridad o manejo de errores sin contaminar la lógica de negocio. Spring Boot lo integra de forma sencilla con solo una dependencia y unas pocas anotaciones.


Referencias e Inspiraciones: