CodeGym /Cursos /Módulo 5. Spring /Configuración de Pointcut y creación de aspectos personal...

Configuración de Pointcut y creación de aspectos personalizados

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

Pointcut — es una expresión que responde a la pregunta "¿dónde exactamente en el código debe activarse el aspecto?". En esencia, es un filtro que elige los métodos necesarios de toda la aplicación. Por ejemplo, "todos los métodos en la capa de servicio" o "métodos con cierta anotación".

En Spring AOP para describir Pointcuts se usa la sintaxis de AspectJ. Sí, al principio puede parecer como código con cifrado RSA, pero en realidad todo tiene sentido. Vamos a desglosarlo.

Expresiones Pointcut integradas

Aquí están las expresiones más básicas que vas a necesitar:

Expresión Descripción
execution Sirve para métodos. Se usa en el 99% de los casos
within Indica el área (por ejemplo, una clase o paquete concreto)
args Útil para métodos con ciertos argumentos
this y target Se refieren al objeto proxy (this) o al objeto objetivo real (target)
bean Útil para beans con un nombre específico

Ejemplo de una expresión Pointcut simple:


@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayerMethods() {
    // Este método es solo un marcador para el Pointcut
}

Vamos a desglosar este "hechizo":

  • execution — nos enfocamos en métodos.
  • * — cualquier tipo de retorno (void, String, int, etc).
  • com.example.service.*.* — cualquier clase en el paquete com.example.service y cualquier método.
  • (..) — cualquier conjunto de parámetros.

Un poco sobre Wildcards

Los Wildcards (comodines) en las expresiones Pointcut nos permiten ser flexibles. Aquí tienes una guía rápida:

  • * — significa "cualquiera". Ejemplo: execution(* com.example..*Controller.*(..)) — cualquier método en cualquier clase que contenga Controller en el nombre.
  • .. — significa "cualquier nivel de anidamiento". Por ejemplo, com.example..service incluye todos los subpaquetes a partir de com.example.

Ejemplo: profundizando en Pointcut

Probemos algo un poco más avanzado. Imagina que quieres añadir logging para todos los métodos de una clase que empiecen por save.


@Aspect
@Component
public class LoggingAspect {

    @Pointcut("execution(* com.example..*Service.save*(..))")
    public void saveMethodsPointcut() {
        // Pointcut para métodos que empiezan con save
    }

    @Before("saveMethodsPointcut()")
    public void beforeSaveAdvice(JoinPoint joinPoint) {
        System.out.println("Llamada al método: " + joinPoint.getSignature().getName());
    }
}

Aquí nosotros:

  • definimos el punto de corte para todos los métodos que empiezan con save en cualquier clase dentro de los subpaquetes de com.example cuyo nombre termina en Service.
  • Antes de la llamada a esos métodos registramos el nombre del método.

Composición de expresiones Pointcut

Ya mencionamos que a veces necesitas combinar varios Pointcuts. Por ejemplo, quieres afectar solo a métodos de un paquete determinado y que además empiecen con cierta palabra.

En AspectJ hay operadores lógicos para composiciones:

  • && — lógico "Y".
  • || — lógico "O".
  • ! — lógico "NO".

Ejemplo:


@Pointcut("within(com.example..*)")
public void withinExamplePackage() {}

@Pointcut("execution(* save*(..))")
public void saveMethods() {}

@Pointcut("withinExamplePackage() && saveMethods()")
public void saveMethodsInExamplePackage() {}

Así, saveMethodsInExamplePackage() se activará solo en métodos que empiecen por save y que estén en paquetes com.example.


Creación de un aspecto personalizado

Ahora apliquemos lo aprendido en la práctica. Supongamos que en nuestra aplicación hay un servicio encargado de gestionar usuarios (UserService). Queremos:

  1. Registrar llamadas a métodos que empiecen por find.
  2. Ejecutar lógica adicional solo cuando el método devuelva un String.

Implementación


@Aspect
@Component
public class CustomAspect {

    // Registro de todos los métodos que empiezan con find
    @Pointcut("execution(* com.example.service.UserService.find*(..))")
    public void findMethods() {}

    // Acción antes de la llamada al método
    @Before("findMethods()")
    public void logBeforeFind(JoinPoint joinPoint) {
        System.out.println("Llamada al método: " + joinPoint.getSignature().getName());
    }

    // Reacción tras la ejecución exitosa del método que devuelve String
    @AfterReturning(
        pointcut = "findMethods()",
        returning = "result"
    )
    public void afterReturningFind(JoinPoint joinPoint, Object result) {
        if (result instanceof String) {
            System.out.println("El método " + joinPoint.getSignature().getName() +
                               " devolvió correctamente el valor: " + result);
        }
    }
}

Qué ocurre:

  1. Con @Pointcut seleccionamos solo los métodos en la clase UserService cuyos nombres empiezan por find.
  2. Usamos @Before para hacer logging de la llamada.
  3. Usamos @AfterReturning para procesar el valor retornado si es una cadena.

Práctica: creación de un aspecto personalizado más complejo

Complicamos un poco la tarea. Supongamos que necesitamos registrar todos los métodos de los controladores que estén anotados con @GetMapping.


@Aspect
@Component
public class ControllerLoggingAspect {

    // Pointcut para métodos anotados con @GetMapping
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void getMappingMethods() {}

    // Registro antes de ejecutar el método
    @Before("getMappingMethods()")
    public void logBeforeGetMapping(JoinPoint joinPoint) {
        System.out.println("Llamada al método del controlador: " + joinPoint.getSignature().getName());
    }
}

Usamos la anotación @annotation para identificar exactamente los métodos objetivo (en este caso, métodos con @GetMapping). ¿Ves lo potente que se vuelve la herramienta?


Depuración y errores típicos

AOP no está libre de sus propios rompecabezas. Aquí tienes algunas cosas a tener en cuenta:

  1. Si tu aspecto no funciona, asegúrate de que la clase del aspecto está anotada con @Component y registrada en el contexto de Spring.
  2. Comprueba que indicaste correctamente la ruta de paquetes en execution. Un nivel de anidamiento omitido o una errata puede arruinarlo todo.
  3. Fíjate en los proxies: si llamas a un método de la clase desde la misma clase, el aspecto puede no activarse.

Comprobación manual de expresiones Pointcut

Si te interesa saber qué métodos exactamente coinciden con tu expresión Pointcut, siempre puedes comprobarlo manualmente:


@Before("execution(* com.example.service.*.*(..))")
public void debugPointcut(JoinPoint joinPoint) {
    System.out.println("Entró en el Pointcut: " + joinPoint.getSignature().getName());
}

Verás en los logs la lista de métodos que coinciden con la expresión.


¡Estás listo para crear aspectos que puedan controlar la lógica de negocio de tu aplicación! En el siguiente paso profundizaremos aún más en los objetos proxy y descubriremos sus secretos ocultos.

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