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
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:
- Spring. Aspect Oriented Programming with Spring. docs.spring.io
- GitHub. lab-1-aop-spring. github.com/martinezhenry/lab-1-aop-spring