Implementación de Logs Personalizados con AOP y Anotaciones en Spring Boot

En el ecosistema de Spring, las anotaciones actúan como metadatos que simplifican la configuración y el comportamiento del código. Comprender cómo funcionan internamente y cómo crear versiones personalizadas mediante Programación Orientada a Aspectos (AOP) es fundamental para desarrollar aplicaciones robustas y mantenibles.

Fundamentos de las Meta-anotaciones

Antes de profundizar en Spring, es necesario entender las meta-anotaciones de Java que definen el comportamiento de nuestras propias anotaciones:

  • @Target: Define dónde se puede aplicar la anotación (clases, métodos, campos, etc.).
  • @Retention: Especifica cuánto tiempo se conserva la anotación (SOURCE, CLASS o RUNTIME). Para enteractuar con ellas mediante reflexión en tiempo de ejecución, se utiliza RetentionPolicy.RUNTIME.
  • @Documented: Indica que la anotación debe ser incluida en la documentación Javadoc.
  • @AliasFor: Una herramienta de Spring para crear alias entre atributos de anotaciones, permitiendo una configuración más flexible.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Auditoria {
   @AliasFor("modulo")
   String value() default "";

   @AliasFor("value")
   String modulo() default "";
}

Anotaciones Esenciales en Spring Boot

Spring ofrecce un conjunto de anotaciones predefinidas que gestionan el ciclo de vida de los objetos y la exposición de servicios:

  • @RestController: Combina @Controller y @ResponseBody, indicando que la clase maneja peticiones web y devuelve datos directamente (usualmente JSON).
  • @Service / @Component: Marcan clases para que sean detectadas por el escaneo de componentes y gestionadas como Beans por el contenedor de Inversión de Control (IoC).
  • @Autowired: Permite la inyección automática de dependencias.
  • @RequestMapping: Mapea rutas URL a métodos específicos. Variantes modernas incluyen @GetMapping y @PostMapping.
  • @RequestBody: Vincula el cuerpo de una petición HTTP con un objeto Java.
  • @ExceptionHandler: Define métodos para capturar y procesar excepciones de manera global o local.

Registro de Actividad mediante Anotaciones Personalizadas y AOP

Una aplicación común de las anotaciones personalizadas es el registro automático de logs para auditoría. Utilizando AOP, podemos interceptar la ejecución de métodos sin ensuciar la lógica de negocio.

1. Definición de la Anotación

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RegistroAcceso {
   String descripcion() default "Sin descripción";
}

2. Configuración de Dependencias (Maven)

Para habilitar el uso de aspectos, es necesario incluir las dependencias de AspectJ en el archivo pom.xml:

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

3. Implementación del Aspecto de Auditoría

El aspecto interceptará los métodos marcados con @RegistroAcceso, capturando los parámetros de entrada y el tiempo de ejecución.

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

@Aspect
@Component
public class AspectoAuditoria {

   private final HttpServletRequest httpServletRequest;

   public AspectoAuditoria(HttpServletRequest request) {
       this.httpServletRequest = request;
   }

   @Around("@annotation(registro)")
   public Object procesarLog(ProceedingJoinPoint puntoDeUnion, RegistroAcceso registro) throws Throwable {
       long inicio = System.currentTimeMillis();
       
       // Ejecución del método original
       Object resultado = puntoDeUnion.proceed();
       
       long duracion = System.currentTimeMillis() - inicio;
       guardarInformacion(puntoDeUnion, registro, duracion);
       
       return resultado;
   }

   private void guardarInformacion(ProceedingJoinPoint joinPoint, RegistroAcceso anotacion, long ms) {
       MethodSignature firma = (MethodSignature) joinPoint.getSignature();
       String clase = joinPoint.getTarget().getClass().getSimpleName();
       String metodo = firma.getName();
       
       System.out.println("--- LOG DE AUDITORÍA ---");
       System.out.println("Módulo: " + anotacion.descripcion());
       System.out.println("Origen: " + clase + "#" + metodo);
       System.out.println("IP Cliente: " + httpServletRequest.getRemoteAddr());
       System.out.println("Tiempo de respuesta: " + ms + "ms");
       System.out.println("Parámetros: " + Arrays.toString(joinPoint.getArgs()));
   }
}

Uso Práctico en un Controlador

Para utilizar esta funcionalidad, simplemente añadimos la anotación sobre el método del controlador que deseamos monitorizar:

@RestController
@RequestMapping("/api/usuarios")
public class ControladorUsuario {

   @RegistroAcceso(descripcion = "Consulta de perfil de usuario")
   @GetMapping("/{id}")
   public ResponseEntity<String> obtenerPerfil(@PathVariable Long id) {
       // Lógica de negocio simulada
       return ResponseEntity.ok("Datos del usuario " + id);
   }
}

Al invocar este endpoint, el aspecto extraerá automáticamente la descripción configurada en la anotación, la dirección IP del solicitante y el tiempo exacto que tardó el servidor en procesar la solicitud, enviando estos datos a la consola o a una base de datos según se requiera.

Etiquetas: Spring Boot AOP Java Annotations AspectJ web development

Publicado el 8-4 13:25