¿Cómo crear un interceptor con anotaciones utilizando Spring Framework y AOP?

En tutoriales anteriores explicábamos cómo crear un interceptor con anotaciones utilizando JEE, el día de hoy hablaremos del mismo procedimiento utilizando Spring Framework y AOP.

¿Cómo iniciamos?

Antes que nada, las dependencias a utilizar:

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.6</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.7.RELEASE</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <type>jar</type>
        </dependency>

 
Y ¿Ahora que viene?

El siguiente paso antes de iniciar en materia es asegurar el logging de nuestra aplicación; para ello crearemos la configuración de log4j con Spring Framework:

# Root logger option
log4j.rootLogger=INFO, stdout

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

 

<?xml version="1.0" encoding="ISO-8859-1"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- Configuracion de LOG -->
    <bean id="log4jInitialization"
          class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
        <property name="targetMethod" value="initLogging" />
        <property name="arguments">
            <list>
                <value>classpath:log4j.properties</value>
                <value>180000</value>
            </list>
        </property>
    </bean>
    <!-- Fin Configuracion de LOG -->
</beans>

 
Entrando en materia

El primer paso para entrar en materia será la creación de nuestra anotación que jugara el papel de punto de acceso a nuestra intercepción:

package com.mm.my.first.spring.interceptor.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Anotaci&oacute;n para el ejemplo.
 * @author ManuelFrancisco
 */
@Target({TYPE, METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFirstAnnotation {
    
    /**
     * Valor para imprimir antes de la ejecuci&oacute;n del m&eacute;todo.
     * @return 
     */
    String beforeExecutionValue();
    
    /**
     * Valor para imprimir despues de la ejecuci&oacute;n del m&eacute;todo.
     * @return 
     */
    String afterExecutionValue();
}

 
En la anotación definimos dos parámetros, los cuales son valores String que utilizaremos antes y después de la ejecución del método. A continuación crearemos nuestra clase interceptora, para ello crearemos dos puntos de acceso, el primero antes de la ejecución del método y el segundo al finalizar la ejecución del mismo:

package com.mm.my.first.spring.interceptor.executor;

import com.mm.my.first.spring.interceptor.annotation.MyFirstAnnotation;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * Clase que realizar&aacute; la intercepci&oacute;n sobre la anotaci&oacute;n.
 * @author ManuelFrancisco
 */
@Component
@Aspect
public class MyFirstInterceptorExecutor {

    /**
     * Logger de la clase.
     */
    private static final Logger LOGGER =
        Logger.getLogger(MyFirstInterceptorExecutor.class);

    /**
     * Se ejecuta antes de ingresar a un m&eacute;todo anotado.
     * @param joinPoint         Punto de intercepci&oacute;n.
     * @param myFirstAnnotation Anotaci&oacute;n.
     */
    @Before("@annotation(myFirstAnnotation)")
    public void beforeExecution(JoinPoint joinPoint,
        MyFirstAnnotation myFirstAnnotation) {

        LOGGER.info("Ejecutando intercepcion antes de la ejecucion. [ " +
            myFirstAnnotation.beforeExecutionValue() + " ]");
    }

    /**
     * Se ejecuta despues de ingresar a un m&eacute;todo anotado.
     * @param joinPoint         Punto de intercepci&oacute;n.
     * @param myFirstAnnotation Anotaci&oacute;n.
     */
    @After("@annotation(myFirstAnnotation)")
    public void afterExecution(JoinPoint joinPoint,
        MyFirstAnnotation myFirstAnnotation) {

        LOGGER.info("Ejecutando intercepcion despues de la ejecucion. [ " +
            myFirstAnnotation.afterExecutionValue() + " ]");
    }
}

 
De nuestra anotación podemos destacar los siguientes aspectos:

  • @Aspect: Anotación que indica que la clase pasa a convertirse en un aspecto.
  • @Before: Indica que el contenido del método se ejecutará antes de ejecutar lo interceptado.
    • @annotation(myFirstAnnotation): Punto de corte, que indica que la intercepción se da al encontrar la anotación.
  • JoinPoint: Variable por default que contiene los diferentes aspectos de la ejecución como son: nombre del método, parámetros, clase, etc.
  • @After: Indica que el contenido del método se ejecutará después de ejecutar lo interceptado.

El siguiente paso será crear nuestra clase interceptada:

package com.mm.my.first.spring.interceptor.test;

import com.mm.my.first.spring.interceptor.annotation.MyFirstAnnotation;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

/**
 * Clase con un m&eacute;todo interceptado.
 * @author ManuelFrancisco
 */
@Component
public class MyInterceptedClass {
    
    /**
     * Logger de la clase.
     */
    private static final Logger LOGGER = 
        Logger.getLogger(MyInterceptedClass.class);
    
    /**
     * M&eacute;todo interceptado.
     */
    @MyFirstAnnotation(beforeExecutionValue = "ANTES", 
        afterExecutionValue = "DESPUES")
    public void interceptedMethod() {
        LOGGER.info("Ejecutando metodo [ interceptedMethod ]");
    }
}

 
A continuación crearemos nuestra clase de configuración para cargar el contexto Spring y en la misma invocaremos el método interceptado:

package com.mm.my.first.spring.interceptor.config;

import com.mm.my.first.spring.interceptor.test.MyInterceptedClass;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.ImportResource;

/*
 * Leemos los archivos de configuraci&oacute;n.
 */
@ImportResource(value = {
    "classpath:applicationContext.xml"
})
/*
 * Cargamos los beans desde los paquetes.
 */
@ComponentScan(basePackages = {"com.mm.my.first.spring.interceptor"},
    excludeFilters = {
        @ComponentScan.Filter(Configuration.class)})
/*
 * Marcamos el bean como un bean de configuraci&oacute;n.
 */
@Configuration
/*
 * Habilita los aspectos.
 */
@EnableAspectJAutoProxy
/**
 * Clase que carga el contexto de Spring.
 */
public class Bootstrap {
    
    /**
     * Logger para la clase.
     */
    private static final Logger LOGGER = Logger.getLogger(Bootstrap.class);
    /**
     * Instancia de la clase interceptada.
     */
    @Autowired
    private MyInterceptedClass myInterceptedClass;

    @PostConstruct
    public void init() {
        LOGGER.info("Ha iniciado el contexto spring.");
        
        // Invocamos el metodo esperando sea interceptado.
        this.myInterceptedClass.interceptedMethod();
    }
    
    @PreDestroy
    public void destroy() {
        LOGGER.info("El contexto esta a punto de ser destruido.");
    }
}

 
Para finalizar crearemos nuestra clase Main para el arranque de la aplicación:

package com.mm.my.first.spring.interceptor.main;

import com.mm.my.first.spring.interceptor.config.Bootstrap;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * Clase por la cual arranca la aplicaci&oacute;n.
 * @author ManuelFrancisco
 */
public class Main {

    /**
     * Logger para la clase.
     */
    private static final Logger LOGGER = Logger.getLogger(Main.class);

    /**
     * M&eacute;todo que inicia la ejecuci&oacute;n de la aplicaci&oacute;n.
     * @param args
     */
    public static void main(String[] args) {
        try {
            initSpringContextWithAnnotations();
        } catch(Exception ex) {
            // TODO: Aqui se colocan las acciones a tomar cuando existe un 
            // error en el arranque.
            
            ex.printStackTrace();
        }
    }

    /**
     * M&eacute;todo encargado de inicializar el contexto de Spring utilizando
     * las anotaciones.
     */
    private static void initSpringContextWithAnnotations() {
        AnnotationConfigApplicationContext appContext =
            new AnnotationConfigApplicationContext(Bootstrap.class);

        appContext.start();

        LOGGER.info("A continuacion registramos el shutdown hook.");
        /*
         * Permite que se ejecuten los metodos anotados con predestroy al
         * finalizar el contexto.
         */
        appContext.registerShutdownHook();
    }
}

 
Al ejecutar la aplicación nos encontraremos el siguiente resultado, donde se evidencia el orden de ejecución de las sentencias y el correcto funcionamiento de nuestro interceptor:

 

MyFirstInterceptorExecutor:34 – Ejecutando intercepcion antes de la ejecucion. [ ANTES ]

MyInterceptedClass:26 – Ejecutando metodo [ interceptedMethod ]

MyFirstInterceptorExecutor:47 – Ejecutando intercepcion despues de la ejecucion. [ DESPUES ]

 

Esto es todo por hoy, el siguiente enlace contiene el código fuente del ejemplo. Espero este tutorial haya sido de su agrado, nos vemos en una próxima entrada.
 

Suscríbete al blog por correo electrónico

Introduce tu correo electrónico para suscribirte a este blog y recibir notificaciones de nuevas entradas.

Deja un comentario