Tutorial Spring AOP

La programación orientada a aspectos (AOP) es un paradigma de programación que intenta formalizar y representar de forma concisa los elementos que son transversales a todo el sistema, uno de estos elementos puede ser el sistema de logging (registro de eventos), mediante la utilización de la AOP es posible insertar código en los componentes ya construidos de nuestra aplicación sin necesidad de modificar el código existente.

El framework Spring utilizar la AOP para muchas de sus funcionalidades, por ejemplo, el manejo de transacciones, el administrador de transacciones intercepta los métodos marcados como transaccionales y crea los respectivos commit y rollback antes y después de ejecutar dichos métodos, otro ejemplo puede ser la seguridad, antes de ejecutar un método se utiliza AOP para comprobar si el usuario tiene los privilegios requeridos para ejecutar dicho método.

La programación orientada a aspectos en Spring puede ser usada mediante archivos de configuración XML o mediante anotaciones, este tutorial utilizara la segunda opción.

Creando un proyecto Spring AOP

Primero creamos un proyecto Maven y agregamos las siguientes dependencias:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.9</version>
    </dependency>
</dependencies>

Para motivos demostrativos, imaginemos que tenemos la siguiente clase, la cual está encargada de realizar las operaciones CRUD, crear, leer, actualizar y eliminar un elemento de una base de datos.

@Component
public class CrudService {

    public void create() {
        System.out.println("crear dato...");
    }

    public void read() {
        System.out.println("leer dato...");
    }

    public void update() {
        System.out.println("actualizar dato...");
    }

    public void delete() {
        System.out.println("eliminar dato...");
    }
}

Ahora, que pasaría si deseemos llevar un registro de la fecha y hora en la que se inicie y termina la ejecución de cada uno de estos métodos, usando programación orientada a objetos (POO) haríamos algo como esto:

public void create() {
    System.out.println("iniciado " + LocalDateTime.now());
    System.out.println("crear dato...");
    System.out.println("finalizado " + LocalDateTime.now());
}

El problema es que debemos hacerlo para cada uno de los métodos, y si tuviésemos más clases con muchos más métodos, el trabajo seria enorme, otro problema está en que le damos a nuestra clase funciones que no le corresponden, veamos como lo solucionamos con AOP.

Lo siguiente que haremos será crear el aspecto encargado de llevar el registro, para ello creamos una clase y usamos la anotación @Aspect sobre ella para declararla como un aspecto, adicionalmente agregaremos @Component para que Spring pueda detectar dicho componente.

package tutor.programacion.aspect;

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

@Aspect
@Component
public class LoggingAspect {

}

Ya podemos definir nuestro punto de corte (pointcuts), esto nos es más que una expresión que define el lugar en donde deseamos insertar un determinado código, para definirlo usamos la anotación @Pointcut, veamos un ejemplo:

@Pointcut("execution(* tutor.programacion.component.*.*(..))")
public void serviceMethod() { }

La expresión debe tener la siguiente forma:

expresión AOP en Spring

execution() el punto de corte debe encajar con la llamada a un método que cumpla con la signatura definida, en nuestro ejemplo: * tutor.programacion.component.*.*(..), en donde el * es un comodín que indica cualquier tipo de clase, los dos puntos al final también son comodines, estos indican cualquier conjunto de argumentos.

Adicionalmente también podemos utilizar el comodín +, el mismo indica cualquier número de caracteres.

En nuestro ejemplo usamos el comodín para sustituir el tipo de retorno, el primero, nombre de clase y método, los dos siguientes, además del conjunto de argumentos, con esto logramos definir un punto de corte para todas las clases y métodos del paquete tutor.programacion.component.

Procedemos ahora, a crear los advices, esto es, el código que se va a ejecutar en un pointcuts, esta ejecución puede darse en las siguientes posiciones usando las respectivas anotaciones, veamos:

  • @Before Antes de la ejecución del método.
  • @AfterReturning Después de la ejecución normal, es decir, si no se genera una excepción.
  • @AfterThrowing Después de producirse una excepción.
  • @After Después de la ejecución, se hayan producido o no excepciones.
  • @Around Antes y después de la ejecución.
@Aspect
@Component
public class LoggingAspect {

    @Pointcut("execution(* tutor.programacion.component.*.*(..))")
    public void serviceMethod() { }

    @Before("serviceMethod()")
    public void beforeLoginAdvice() {
        System.out.println("iniciado " + LocalDateTime.now());
    }

    @After("serviceMethod()")
    public void afterLoginAdvice() {
        System.out.println("finalizado " + LocalDateTime.now());
    }
}

Nuestro ejemplo demostrativo utiliza @Before para capturar el tiempo en que se inicia la ejecución del método y @After para registrar la finalización del mismo, para hacer referencia al pointcut usamos el nombre del método, es posible colocar la expresión en las anotaciones antes mencionadas, esto si solo la utilizaremos una vez.

La clase principal no tiene nada que no conozcamos de tutoriales anteriores, salvo la anotación usada para habilitar el proxy AOP y las anotaciones AspectJ, ella es @EnableAspectJAutoProxy, seguida hacemos lo de siempre, creamos el contexto y obtenemos el bean, luego ejecutamos los métodos CRUD de nuestra clase.

@Configuration
@ComponentScan({"tutor.programacion.aspect", "tutor.programacion.component"})
@EnableAspectJAutoProxy
public class TutorialAOP {
    
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(TutorialAOP.class);
        
        CrudService ps = ctx.getBean(CrudService.class);
        
        ps.create();
        ps.read();
        ps.update();
        ps.delete();
    }
}

Tutorial AOP en Spring Framework

SI en determinado caso requerimos mas información sobre el método que se ha interceptado, podemos modificar el advice, solo debemos agregar un parámetro de tipo JointPoint al método, este objeto contendrá toda la información necesaria, veamos un ejemplo.

@Before("serviceMethod()")
public void beforeLoginAdvice(JoinPoint jp) {
    System.out.println("\n" + jp.toLongString());
    System.out.println("iniciado " + LocalDateTime.now());
}

Al llamar a jp.toLongString() obtenemos una descripción completa de la expresión utilizada para definir el pointcut actual, podemos obtener más información, como: los argumentos del método, entre otras cosas.

AOP anotación @Before

De momento quedamos aquí, en próximos tutoriales veremos en detalle todas estas anotaciones, haremos cosas como: modificar un argumente antes de pasarlo a al método, obtener el valor devuelto, trabajar con excepciones, etc., todo esto más adelante. 

Descargar proyecto: spring-aop-introduccion.zip

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

Controles y Contenedores JavaFX 8 - I

tkinter Canvas

Histogramas OpenCV Python