lunes, 30 de septiembre de 2013

Spring: AOP con Aspect mediante archivos xml y Anotation

Recorriendo Spring
Existen diferentes formas de trabajar con aspectos en Spring AOP  (mediante XML, mediante anotaciones AspectJ, utilizando configuración por Java, utilizando configuración por XML, con CGLib, etc…)  Nosotros nos centraremos en Spring AOP con anotaciones AspectJ, utilizando proxies que envuelven a las clases y ejecutan los aspectos. Hay que tener en cuenta que, por defecto, Spring AOP sólo permite definir aspectos sobre clases que implementen algún interfaz ya que para ello utiliza proxies dinámicos de JDK.


Lo primero que debemos aclarar como lo mencione en anteriores entregas, el manejo de aspectos por notaciones es mucho mas comodo y mucho mas simple, por que la responsabilidad de definir los interceptores y los patrones bajo los cuales deben ser ejecutados no estará en nuestro archivo spring bean configuration, sino que lo trasladaremos a la clase que tiene la implementacion de los advise, el proyecto y las clases sobre las que trabajaremos son las del proyecto que utilizamos en el tutorial anterior a fin de poder abordar en mayor detalle la mecanica Aop con notaciones y no demorar en entender las clases del ejemplo.

Nuestra estructura de proyecto no presenta grandes complicaciones.








































Nuestro archivo POM no presentara grandes diferencias en cuanto al que armamos en anteriores entregas, por lo que el  siguiente paso es definir nuestro archivo POM


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>Aop_Nativo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
   <spring.version>3.0.5.RELEASE</spring.version>
  </properties>
  <repositories>
 <repository>
     <id>java.net</id>
     <url>https://repo.maven.apache.org/maven2/</url>
 </repository>
    </repositories>
  <dependencies>
   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
   </dependency>
   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
   </dependency>
   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
   </dependency>
   <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.6</version>
   </dependency>
   <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.6.11</version>
   </dependency>
  </dependencies>
</project>

Donde declaro las bibliotecas que utilizaremos, para esta ocasión trabajaremos con la version 3.0.5.RELEASE para lo que definimos una propiedad con ese fin. 

Incluimos las librerías comunes para trabajar con SPRING 
  • spring-core
  • spring-context
Para trabajar con AOP utilizaremos 
  • spring-aop
  • aspectjweaver
  • aspectjrt

De manera inicial es una estructura simple inicial de todo proyecto spring, donde todo nace lógicamente de la definición del pojo.

com.company.modelo.Usuario.java:   como podemos ver es solo una entidad con sus getter y setter como lo indica spring.


package com.mycompany.modelo;

public class Usuario {

 private String nombre;
 private String apellidos;
    private String direccion;
    
    public String getNombre() {
  return nombre;
 }
 public void setNombre(String nombre) {
  this.nombre = nombre;
 }
 public String getApellidos() {
  return apellidos;
 }
 public void setApellidos(String apellidos) {
  this.apellidos = apellidos;
 }
 public String getDireccion() {
  return direccion;
 }
 public void setDireccion(String direccion) {
  this.direccion = direccion;
 }
 
    
}

el siguiente paso es definir los contratos o interfaces sobre las que se desarrollaran implementaciones.

com.company.services.UsuarioService.java: es solo nuestro contrato donde definimos los métodos que serán implementado,  mucho de los metodos estan definidos claramente para poder mostrar las virtudes de AOP.


package com.company.services;

import com.mycompany.modelo.Usuario;

public interface UsuarioService {
 public Usuario consultaUsuario( Usuario usuario );
 public Usuario consultaUsuario_alternativo( Usuario usuario );
    public boolean agregarUsuario( Usuario usuario );
    public int actualizarUsuario( Usuario usuario );
    public boolean borrarUsuario( Usuario usuario );
    public void procesarInformacion();
    public void ProbarThrowException() throws Exception;
}


El siguiente paso sera definir la implementacion  de la interfaz.

com.company.services.UsuarioServiceImpl.java:  como podemos observar no tiene mucho código y funcionalidad dado que no es el objetivo de este tutorial. Contiene métodos que reciben objetos y retornan resultados y que lo unico que hacen es imprimir por consola el paso sobre el metodo.


package com.company.services;

import com.mycompany.modelo.Usuario;

public class UsuarioServiceImpl implements UsuarioService {

 public Usuario consultaUsuario( Usuario usuario ) {
        System.out.println( "Consultando el Usuario : " + usuario.getNombre() + " " + usuario.getApellidos());
        return usuario;
    }
 
 public Usuario consultaUsuario_alternativo( Usuario usuario ) {
        System.out.println( "Consultando el Usuario alternativo : " + usuario.getNombre() + " " + usuario.getApellidos());
        return usuario;
    }

    public boolean agregarUsuario( Usuario usuario ) {
        System.out.println( "Insertando Usuario : " + usuario );
        return true;
    }

    public int actualizarUsuario( Usuario usuario ) {
        System.out.println( "Actualizando Usuario : " + usuario.getNombre() );
        return 1;
    }

    public boolean borrarUsuario( Usuario usuario ) {
        System.out.println( "Borrando Usuario : " + usuario.getNombre() );
        return true;
    }

    public void ProbarThrowException() throws Exception {
  System.out.println("ProbarThrowException() esta corriendo ");
  throw new Exception("Generic Error");
 }
    
    public void procesarInformacion() {

        try {

            Thread tarea = new Thread( new Runnable() {
                public void run() {}
            }
            );
            tarea.start();
            tarea.sleep( 5000 );
        }
        catch( Exception e ) {
            e.printStackTrace();
        }

    }

}


Lo siguiente es lo normal trabajar en nuestro spring bean configuration o nuestro archivo contenedor de beans,  Lo primero que haremos será permitir el uso de aspectos mediante la etiqueta:  <aop:aspectj-autoproxy/>

src/main/resources/usuarioe.xml:  


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<aop:aspectj-autoproxy />

 <!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioServiceTarget" class = "com.company.services.UsuarioServiceImpl"/>
  <!-- Definicion del consejo--> 
   <bean id = "logAspect" class = "com.company.aop.LoggingAspect"/>  
</beans>

Nota: aquí es donde viene lo loco, en mi archivo de configuración solo voy a definir 2 bean 

el primero que hace referencia al bean de la clase que implementaremos 

<!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioServiceTarget" class = "com.company.services.UsuarioServiceImpl"/>

el segundo hace referencia a la clase que va implementar los advise.

<!-- Definicion del consejo--> 
   <bean id = "logAspect" class = "com.company.aop.LoggingAspect"/>  

cabe destacar que la lógica de ejecución de los advise en el ejemplo anterior estaba en el archivo de configuración y ahora va estar en la clase de implementacion.

nuestro archivo de test que consume por supuesto los beans y que ejecuta los métodos de la interfaz.

com.company.test.UsuarioServiceTest.java:  es una clase muy simple que solo genera el aplicationcontext, busca el archivo de configuración que utilizaremos y solicita el retorno del bean que utilizaremos y por ultimo ejecuta una serie de metodos.


package com.company.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.company.services.UsuarioService;
import com.mycompany.modelo.Usuario;

public class UsuarioServiceTest {
 

    public static void main(String[] args) throws Exception {
     ApplicationContext ctx;        
     ctx = new FileSystemXmlApplicationContext("src/main/resources/usuarioe.xml");
        UsuarioService service = (UsuarioService)ctx.getBean( "usuarioService" );
        
        Usuario usuario = new Usuario();
        usuario.setNombre( "diego" );
        usuario.setApellidos( "herrera" );
        usuario.setDireccion( "loria 134" );
        
        service.consultaUsuario( usuario );
        service.borrarUsuario(usuario);
        service.consultaUsuario_alternativo(usuario);
        service.actualizarUsuario(usuario);
        service.procesarInformacion();
        service.ProbarThrowException();
    }

    
}

Nota: podemos notar que crean un objeto, cargado desde el código solo con fines de contar con datos para poder trabajar. Es decir hacemos esto para simular datos.

ahora llega la parte importante por que toda la logica y notaciones de ejecución de los advice esta en la clase que implementa los advice lo que es mucho mas practico en proyecto de gran alcance.

Ahora pero la diferencia solo esta en que antes trabajaba con el archivo bean de configuracion y ahora meto todo en la clase que implementa los interceptores ??


Spring AOP utiliza proxys dinámicos de manera que basta con la JDK para usar Spring AOP mientras que AspectJ cambia el .class de la clase para añadirle los aspectos que programemos así es necesario el uso de la librería de AspectJ. 



que !! si funciona de manera diferente pero amparado en los mismos conceptos, toma nota.

Un sistema final se va descomponer en clases y aspectos,  la relación entrega clases y aspectos  va estar asociado a los conceptos de joinpoint y weaving.

Un Join Poin indica un posible punto del sistema donde la implementación del aspecto puede insertarse.

Un aspecto weaver tiene la responsabilidad de procesar el lenguaje base y el lenguaje de aspecto y componerlos con el objetivo de obtener la implementación final del  sistema.

“En pocas palabras weaver utiliza el joinpoint para armar un código que tendrá la implementación del aspecto y el código fuente original”.

Pero te lo pongo mas fácil en una formula:

Join point  +  pointcuts + advise  = aspect

Mas simple imposible imposible:
  • Join point: punto de ejecución.
  • Pointcut: predicados que atrapan los join point  es decir la condición para la que se dispara.
  • Advise: es la acción a realizar dado un PointCut.


En Spring AOP sólo se pueden aplicar aspectos cuando un método es llamado mientras que en AspectJ se pueden aplicar a un método cuando es llamado, cuando se está ejecutando, a todo un paquete, y también se pueden definir precondiones y postcondiciones a los apectos de tal manera que no se puedan programar métodos que no cumplan con las condiciones de los aspectos que se les asocian.

Si recuerdan en el ejemplo spring Aop,  para la implementacion de los advise definimos una clase para cada implementacion de advise, pero lo cierto es que lo común es que se use una sola clase implementando las diferentes interfaces que necesitamos para cada advise un ejemplo podría ser algo como:

Ejemplo mas real de implementacion de advise utilizando spring Aop 


package info.hcosta.ejercicioaop;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
 
public class LoggerAdvice 
implements MethodBeforeAdvice
, AfterReturningAdvice
, MethodInterceptor
, ThrowsAdvice {
 
 
    public void before(Method method, Object[] args, Object target) throws Throwable {
      ............
    }
 
    public void afterReturning(Object o, Method method, Object[] args, Object object) throws Throwable {
       ..........
    }
 
    public void afterThrowing(Exception e) throws Throwable {
     ............
    }
    public Object invoke(MethodInvocation arg0) throws Throwable {
    .............
    }
    ..........
}

Nota: Quería mostrar esta alternativa para que la forma en que implementamos nuestros advise no parezca tan estraña y pueda ser familiar.

Pero regresemos a nuestro ejemplo puntual de la implementacion de aspectos de este ejemplo que es donde nos vamos a demorar un poco explicando su contruccion y por supuesto podremos tener una mirada a las notaciones que nos proporciona aspectj para trabajar con aspectos.

com.company.aop.LoggingAspect : es nuestra clase definida para implementar el aspecto.


package com.company.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoggingAspect {

 @Before("execution(* com.company.services.UsuarioService.consultaUsuario(..))")
 public void logBefore(JoinPoint joinPoint) {
  System.out.println("*********************ADVISE BEFORE*******************");
  System.out.println("metodo logBefore() esta corriendo!");
  System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
  System.out.println("*****************************************************");
 }
 
 @After("execution(* com.company.services.UsuarioService.consultaUsuario(..))")
 public void logAfter(JoinPoint joinPoint) {
  System.out.println("****************ADVISE AFTER*************************");
  System.out.println("metodo logAfter() esta corriendo!");
  System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
  System.out.println("******************************************************");
 
 }
 
 @AfterReturning(
        pointcut = "execution(* com.company.services.UsuarioService.agregarUsuario(..))",
        returning= "result")
     public void logAfterReturning(JoinPoint joinPoint, Object result) {
      System.out.println("****************ADVISE AFTERRETURN********************");
   System.out.println("metodo logAfterReturning() esta corriendo!");
   System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
   System.out.println("El metodo retorna el valor : " + result);
   System.out.println("******************************************************");
   
     }
 
 @AfterThrowing(
        pointcut = "execution(* com.company.services.UsuarioService.ProbarThrowException(..))",
        throwing= "error")
      public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
      System.out.println("****************ADVISE AfterThrowing********************");
   System.out.println("metodo logAfterThrowing() esta corriendo!");
   System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
   System.out.println("Exception : " + error);
   System.out.println("********************************************************");
   
      }
 
 @Around("execution(* com.company.services.UsuarioService.procesarInformacion(..))")
    public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
  System.out.println("****************ADVISE Around ****************************");
  System.out.println("metodo logAround() is running!");
  System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
  //System.out.println("parametros de entrada : " + Arrays.toString(joinPoint.getArgs()));
  
  System.out.println("Ejecucion antes del procesamiento del metodo!");
  joinPoint.proceed(); //continue on the intercepted method
  System.out.println("Ejecucion luego del procesamiento del metodo!");
  
  System.out.println("***********************************************************");
  
    }
}


la primera observación que podemos realizar es la forma en que definimos un aspecto, o sea en primera instancia generamos una clase que contendrá las implementaciones y agregamos la notación @Aspect.

Lo siguiente en lo que quiero detenerme es como se define la condición para la que se ejecuta un advise que lo llamamos pointcut.

Para definir pointcuts en Spring AOP utilizaremos la notación de AspectJ y seguirán el siguiente patrón: execution() Para que un método sea “interceptado” por un aspecto, deberá cumplir el patrón que indiquemos. Además, podemos componer pointcuts utilizando && (and), || (or) y ! (not).

[Modificadores] TipoRetorno [Clase] NombreMétodo ([Parámetros]) [throws TipoExcepción]

pero miremos un ejemplo de patron y lo analicemos

execution(* com.company.services.UsuarioService.consultaUsuario(..))


  1. * cualquier tipo de retorno
  2. com.company.services.UsuarioService  paquete e interfaz que contiene el metodo a interceptar.
  3. consultaUsuario metodo que buscaremos interceptar 
  4.  .. aceptando 0 a varios parametros 


Hay que recordar que los métodos que vayan a ser seleccionados por un pointcunt deben ser visibles (públicos).


  • execution(void send*(String)): cualquier método visible que comience por send, tome un String como único parámetro y cuyo tipo de retorno sea void.
  • execution(* send(*)): cualquier método visible llamado send que tome como parámetro un parámetro de cualquier tipo.
  • execution(* send(int, ..)): cualquier método visible llamado send que tome al menos un parámetro de tipo int. En este caso “..” indica 0 o más.
  • execution(void org.ejemplo.MessageServiceImpl.*(..)): cualquier método visible de la clase org.ejemplo.MessageServiceImpl que tenga como tipo de retorno void. 
  • execution(void org.ejemplo.MessageService+.send(*)): cualquier método visible con nombre send de las clases del tipo org.ejemplo.MesssageService, incluyendo hijos e implementaciones, que reciban un único parámetro de cualquier tipo y tengan void como tipo de retorno.
  • execution(@javax.annotation.security.PermitAll void send*(..)): cualquier método visible que comience por send y que esté anotado con la anotación @PermitAll.
  • execution(* org.ejemplo.*.impl.*.*(..)): cualquier método visible de cualquier clase de cualquier paquete impl situada dos escalones por debajo de org.ejemplo en la jerarquía de paquetes.
  • execution(* org.ejemplo..impl.*.*(..)): cualquier método visible de cualquier clase de cualquier paquete impl situada cualquier nivel por debajo por debajo de org.ejemplo en la jerarquía de paquetes. En este caso “..” indica que puede haber 0 o más directorios en la jerarquía de paquetes.
En fin podemos observar que podemos jugar mucho con la condición en la que se ejecutara un aspecto por lo que son infinitas las posibilidades para la utilización de interceptores.

Ahora si aplico la regla antes descrita vamos a entender como se implementan los advise de mi clase.

Join point  +  pointcuts + advise  = aspect


implementando advise @before

@Before("execution(* com.company.services.UsuarioService.consultaUsuario(..))")
 public void logBefore(JoinPoint joinPoint) {
  System.out.println("*********************ADVISE BEFORE*******************");
  System.out.println("metodo logBefore() esta corriendo!");
  System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
  System.out.println("*****************************************************");
 }

Nota: primero que utilizamos la notación @before, segundo que definimos nuestro pointcut que es la condición en la que ejecutaremos y por ultimo la implementacion del advise  que debería contar con la información del punto de ejecución de la aplicación para lo que pasamos un parametro de tipo joinpoint a la implementacion.

Si recordamos este advise se ejecuta antes del procesamiento del método interceptado, segun mi pointcut sera sobre el metodo consultarusuario. A nivel código no presenta grandes problemáticas dado que no es el objeto del tutorial, solo imprime por consola su paso por el metodo y un dato nos demuestra como acceder a estructuras de datos desde el parametro de tipo joinpoint que en este caso solo la utilizamos para obtener la informacion del metodo que interceptamos.

Implementando advise @After

@After("execution(* com.company.services.UsuarioService.consultaUsuario(..))")
 public void logAfter(JoinPoint joinPoint) {
  System.out.println("****************ADVISE AFTER*************************");
  System.out.println("metodo logAfter() esta corriendo!");
  System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
  System.out.println("******************************************************");
 
 }

Nota: Primero que utilizamos la notación @after, segundo definimos nuestro pointcut que nos indica que trabajara sobre el método consultaUsuario() y para finalizar la implementacion del advise. Recordemos que este advise es el que se ejecuta finalizado el procesamiento del metodo interceptado. Recibe un parámetro joinpoint  para obtener la información del punto de ejecución.

Implementando advise @AfterReturning


@AfterReturning(
        pointcut = "execution(* com.company.services.UsuarioService.agregarUsuario(..))",
        returning= "result")
     public void logAfterReturning(JoinPoint joinPoint, Object result) {
      System.out.println("****************ADVISE AFTERRETURN********************");
   System.out.println("metodo logAfterReturning() esta corriendo!");
   System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
   System.out.println("El metodo retorna el valor : " + result);
   System.out.println("******************************************************");
   
     }

Nota: Primero utilizamos la notacion @AfterReturning para definir el tipo del advise, pero una particularidad este advise se ejecuta luego del procesamiento del metodo interceptado y tiene la particularidad que puede trabajar con lo que retorna el metodo luego de su procesamiento por lo que este advise requiere aparte del pointcut indicar el retorno, por ello podremos notar que los parametros de entrada a la implementacion son el clasico joinpoint y el objeto de retorno con el cual poder trabajar.  Para este caso el metodo que vamos a interceptar es agregarusuario.

Implementando advise @AfterThrowing


@AfterThrowing(
        pointcut = "execution(* com.company.services.UsuarioService.ProbarThrowException(..))",
        throwing= "error")
      public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
      System.out.println("****************ADVISE AfterThrowing********************");
   System.out.println("metodo logAfterThrowing() esta corriendo!");
   System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
   System.out.println("Exception : " + error);
   System.out.println("********************************************************");
   
      }

Nota: Primero utilizamos la notación @AfterThrowing para definir el tipo de advise, recordemos que este permite capturar las excepciones que ocurran en un metodo y nosotros a nivel codigo en el metodo a interceptar estamos generando una excepcion por codigo para verificar el funcionamiento, la diferencia puntual que existe con el tradicion spring aop es que si recuerdan tenia que implementar para cada excepcion que deseara capturar el metodo con el parametro de entrada, en nuestro caso con aspectj ya no es necesario , sea cual sea la excepcion que se dispare entrare a este metodo interceptor, ahora dentro del mismo podre identificar la tipologia trabajando con el parametro de entrada error que me incluye la implementacion.

Implementando advise @Around


@Around("execution(* com.company.services.UsuarioService.procesarInformacion(..))")
    public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
  System.out.println("****************ADVISE Around ****************************");
  System.out.println("metodo logAround() is running!");
  System.out.println("metodo interceptado : " + joinPoint.getSignature().getName());
  //System.out.println("parametros de entrada : " + Arrays.toString(joinPoint.getArgs()));
  
  System.out.println("Ejecucion antes del procesamiento del metodo!");
  joinPoint.proceed(); //continue on the intercepted method
  System.out.println("Ejecucion luego del procesamiento del metodo!");
  
  System.out.println("***********************************************************");
  
    }

Nota: sobre este no vamos a hablar mucho solo que se ejecuta sobre el metodo procesar información donde simulamos tiempo de procesamiento con hilos buscando mostrar que trabajamos antes del procesamiento y después del procesamiento del metodo interceptado y nos posibilita decidir en base al escenario si ejecutamos el metodo o no.

el resultado de la ejecución posibilita de una manera mas visual el orden de ejecución de métodos e interceptores.




Para finalizar quiero recalcar que existe una manera muy comoda de trabajar con el archivo spring bean configuration con aspectj que pueden buscarla en internet como otra alternativa de trabajo. Recomiendo este articulo que muestra un paralelismo sobre como hacerlo con notaciones y mediante archivo.

El codigo fuente del proyecto lo puedes descargar aqui codigofuente.zip

sábado, 28 de septiembre de 2013

Spring: AOP Programación Orientada a Aspectos

Recorriendo Spring
Es un paradigma de programación relativamente reciente cuya intención es permitir una adecuada modularización de las aplicaciones y posibilitar una mejor separación de incumbencias cuyo  principal objetivo es la separación de las funcionalidades dentro del sistema:
  • Por un lado funcionalidades comunes utilizadas a lo largo de la aplicación.
  • Por otro lado, las funcionalidades propias de cada módulo.
Nota: AOP busca poder tratar de extraer como módulos aquel código que resuelve problemas que son transversales a los componentes de una aplicación.

Whats ¿?

Son funcionalidades genéricas que se utilizan en muchos puntos diferentes de nuestra aplicación. Algunas de las más comunes son: logs, transacciones, seguridad, cachés, manejo de errores, monitorización
Si amigo se trata de las responsabilidades de un componente, un objeto de una clase servicio debería preocuparse de sus responsabilidades principales, pero sin embargo se encuentra lleno de código que excede su responsabilidad, generando código repetido y ensuciando los métodos por la mezcla.


Caso de Análisis 1: Imaginemos una implementación de servicio donde se requiere que usuario tenga un determinado rol para realizar todas las operaciones que puede exponer.  incluiría una verificación de permisos antes de ejecutar los métodos expuesto en la service layer.

public class PatientServiceImpl implements PatientService{
...

 public Patient findById(Integer patientId) {
   if(!hasPermission(SecurityContext.getPrincipal())) {
     throw new AccessDeniedException();
   }
   return patientDao.findById(patientId);
 }
 public List findByRoom(Integer roomId) {
   if(!hasPermission(SecurityContext.getPrincipal())) {
     throw new AccessDeniedException();
   }
   return patientDao.findByRoom(roomId);
 }

}

Este código tiene dos problemas:
  • ·         Mezcla y acopla conceptos que son diferentes: los métodos findById y findByRoom deben preocuparse de encontrar los datos, no de gestionar la seguridad. Esto es lo que conocemos como “code tangling” (enredo de código).
  • ·         La solución a un mismo problema aparece repetida varias veces en diferentes partes de la aplicación: el código que comprueba el rol de usuario está repetido en diferentes puntos. Esto es lo que conocemos como “code scattering” (dispersión de código).

Nota: En una aplicación pequeña puede que esto no suponga un gran problema pero a medida que nuestra aplicación crece, es muy costoso mantener código disperso cuya funcionalidad, además, está entremezclada con otras.


Caso de Análisis 2:  Uno de los ejemplos más famosos para ver el funcionamiento de los aspectos es el de un Logger. El Logger lo implementamos como una clase de nuestra aplicación cuya función suele ser narrar y guardar los eventos, excepciones que suceden en las demás clases.

Clase Persona.java

package info.hcosta.poa.ejemplo;

/**
 *
 * @author Administrador
 */
public class Persona implements IPersona {
     
    private Logger logger = new Logger();
    private String nombre;
     
    public void setNombre(String nombre){
        this.nombre = nombre;
    }
     
    public String getNombre(){
        return this.nombre;
    }
     
    public void saludar() {
        logger.antesSaludo(this);
        System.out.println( this.nombre +" dice: - Hola que tal?");
        logger.despuesSaludo(this);
    }

}

Nota: como podemos ver la clase persona utiliza una clase logger para registrar eventos antes y después de la ejecución del método de responsabilidad en si del componente.

Clase logger.java

package info.hcosta.poa.ejemplo;

import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 *
 * @author Administrador
 */
public class Logger {
     
    private Calendar cal = new GregorianCalendar();
     
    public void antesSaludo(Persona persona) {
        System.out.println(cal.getTime() + " " + persona.getNombre() + " va a saludar.");
    }
     
    public void despuesSaludo(Persona persona) {
        System.out.println(cal.getTime() + " " + persona.getNombre() + " ha saludado.");
    }
     
}

En definitiva lo que estamos haciendo es hacer la clase Logger dependiente de Persona. Imagina que tenemos una aplicación con 50 clases y todas necesitan un Logger para apuntar un evento. En el peor de los casos instanciaríamos el Logger 50 veces y llamaríamos las funciones del log desde el propio flujo de los métodos de las clases. Sería un gran trabajo y además estaríamos repitiendo mucho código.

AOP es una solución muy elegante para eliminar estos problemas en  tres pasos:
  • ·         Implementa la lógica de negocio de tu aplicación.
  • ·         Implementa aspectos que resuelvan los problemas transversales a tu aplicación.
  • ·         Enlaza estos aspectos en los puntos en los que sean necesarios.
Ahora para entender esta solución mágica es necesario conocer algunos conceptos utilizados por spring.

Aspect (Aspecto) es una funcionalidad transversal (cross-cutting) que se va a implementar de forma modular y separada del resto del sistema. El ejemplo más común y simple de un aspecto es el logging (registro de sucesos) dentro del sistema, ya que necesariamente afecta a todas las partes del sistema que generan un suceso.

Join point (Punto de Cruce o de Unión) es un punto de ejecución dentro del sistema donde un aspecto puede ser conectado, como una llamada a un método, el lanzamiento de una excepción o la modificación de un campo. El código del aspecto será insertado en el flujo de ejecución de la aplicación para añadir su funcionalidad.

Advice (Consejo) es la implementación del aspecto, es decir, contiene el código que implementa la nueva funcionalidad. Se insertan en la aplicación en los Puntos de Cruce.

Quiero dejar claro que en  Spring hay varios tipos de Advices, y según cuál de ellos queramos implementar hay que implementar una u otra interface :
  • Before : Es fácil de intuir que este advice se ejecutará   siempre antes de la llamada a un método.
  • After Returning :  Este advice se ejecuta después de la llamada a un método.
  • Around : Cuando  implementamos un advice de este tipo podremos realizar acciones antes y después de invocar el método interceptado.
  • Throws : Se ejecuta cuando  ocurre una excepción.

Pointcut (Puntos de Corte) define los Consejos que se aplicarán a cada Punto de Cruce. Se especifica mediante Expresiones Regulares o mediante patrones de nombres (de clases, métodos o campos), e incluso dinámicamente en tiempo de ejecución según el valor de ciertos parámetros.
El objetivo sera que mediante ejemplos prácticos pueda abordar las diferentes opciones que brinda spring para trabajar en AOP.
  • Mediante el Spring Bean configuration
  • Mediante el Spring Bean Configuration con Aspctj.
  • Mediante notaciones con Aspectj.

Trabajando los advise

En primera instancia la idea es comenzar con los Advise, por lo que trabajaremos en un ejemplo practico basado en la simulación de un crud (create read update delete) de una clase usuario, no vamos a desarrollar grandes código dado que el objeto de estudio es AOP.


Nota: En lo personal me parece mas aceptable la utilización de anotaciones, pero es cierto que utilizando aop nativo por archivo de configuración te permite entender los conceptos de una manera mas clara lo que posibilitara que la curba de aprendizaje de anotaciones se corta.

Como en todo nuestros ejemplos vamos a trabajar con  proyectos simples en Maven. 


Generamos nuestro proyecto de tipo Maven



Elegimos la tipologia mas simple que nos generara una estructura de proyecto general.



Proporcionamos información del proyecto.



El siguiente paso es definir nuestro archivo POM


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>Aop_Nativo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
   <spring.version>3.0.5.RELEASE</spring.version>
  </properties>
  <repositories>
 <repository>
     <id>java.net</id>
     <url>https://repo.maven.apache.org/maven2/</url>
 </repository>
    </repositories>
  <dependencies>
   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
   </dependency>
   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
   </dependency>
   <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
   </dependency>
   <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.6</version>
   </dependency>
   <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.6.11</version>
   </dependency>
  </dependencies>
</project>

Donde declaro las bibliotecas que utilizaremos, para esta ocasión trabajaremos con la version 3.0.5.RELEASE para lo que definimos una propiedad con ese fin. 

Incluimos las librerías comunes para trabajar con SPRING 
  • spring-core
  • spring-context
Para trabajar con AOP utilizaremos 
  • spring-aop
  • aspectjweaver
  • aspectjrt

Nota: para este ejemplo con solo incluir la primera spring-aop alcanza, pero incluyo las otras dos por que la utilizaremos en la utilización  de aspectj en futuros ejemplos.

la estructura inicial con la que vamos a trabajar es la siguiente:





De manera inicial es una estructura simple inicial de todo proyecto spring, donde todo nace lógicamente de la definición del pojo.

com.company.modelo.Usuario.java:   como podemos ver es solo una entidad con sus getter y setter como lo indica spring.


package com.mycompany.modelo;

public class Usuario {

 private String nombre;
 private String apellidos;
    private String direccion;
    
    public String getNombre() {
  return nombre;
 }
 public void setNombre(String nombre) {
  this.nombre = nombre;
 }
 public String getApellidos() {
  return apellidos;
 }
 public void setApellidos(String apellidos) {
  this.apellidos = apellidos;
 }
 public String getDireccion() {
  return direccion;
 }
 public void setDireccion(String direccion) {
  this.direccion = direccion;
 }
 
    
}

el siguiente paso es definir los contratos o interfaces sobre las que se desarrollaran implementaciones.

com.company.services.UsuarioService.java: es solo nuestro contrato donde definimos los métodos que serán implementado,  mucho de los metodos estan definidos claramente para poder mostrar las virtudes de AOP.


package com.company.services;

import com.mycompany.modelo.Usuario;

public interface UsuarioService {
 public Usuario consultaUsuario( Usuario usuario );
 public Usuario consultaUsuario_alternativo( Usuario usuario );
    public boolean agregarUsuario( Usuario usuario );
    public int actualizarUsuario( Usuario usuario );
    public boolean borrarUsuario( Usuario usuario );
    public void procesarInformacion();
    public void ProbarThrowException() throws Exception;
}


El siguiente paso sera definir la implementacion  de la interfaz.

com.company.services.UsuarioServiceImpl.java:  como podemos observar no tiene mucho código y funcionalidad dado que no es el objetivo de este tutorial. Contiene métodos que reciben objetos y retornan resultados y que lo unico que hacen es imprimir por consola el paso sobre el metodo.


package com.company.services;

import com.mycompany.modelo.Usuario;

public class UsuarioServiceImpl implements UsuarioService {

 public Usuario consultaUsuario( Usuario usuario ) {
        System.out.println( "Consultando el Usuario : " + usuario.getNombre() + " " + usuario.getApellidos());
        return usuario;
    }
 
 public Usuario consultaUsuario_alternativo( Usuario usuario ) {
        System.out.println( "Consultando el Usuario alternativo : " + usuario.getNombre() + " " + usuario.getApellidos());
        return usuario;
    }

    public boolean agregarUsuario( Usuario usuario ) {
        System.out.println( "Insertando Usuario : " + usuario );
        return true;
    }

    public int actualizarUsuario( Usuario usuario ) {
        System.out.println( "Actualizando Usuario : " + usuario.getNombre() );
        return 1;
    }

    public boolean borrarUsuario( Usuario usuario ) {
        System.out.println( "Borrando Usuario : " + usuario.getNombre() );
        return true;
    }

    public void ProbarThrowException() throws Exception {
  System.out.println("ProbarThrowException() esta corriendo ");
  throw new Exception("Generic Error");
 }
    
    public void procesarInformacion() {

        try {

            Thread tarea = new Thread( new Runnable() {
                public void run() {}
            }
            );
            tarea.start();
            tarea.sleep( 5000 );
        }
        catch( Exception e ) {
            e.printStackTrace();
        }

    }

}

Lo siguiente es lo normal trabajar en nuestro spring bean configuration o nuestro archivo contenedor de beans,

src/main/resources/usuarioe.xml:  de manera inicial el unico bean sera el asociado a la clase que implementa las funcionalidades UsuarioServiceImpl.java


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

 <!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioService" class = "com.company.services.UsuarioServiceImpl"/>
   
    
</beans>

por ultimo nuestro archivo de test que consume por supuesto los beans y que ejecuta los metodos de la interfaz.

com.company.test.UsuarioServiceTest.java:  es una clase muy simple que solo genera el aplicationcontext, busca el archivo de configuración que utilizaremos y solicita el retorno del bean que utilizaremos y por ultimo ejecuta una serie de metodos.


package com.company.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.company.services.UsuarioService;
import com.mycompany.modelo.Usuario;

public class UsuarioServiceTest {
 

    public static void main(String[] args) throws Exception {
     ApplicationContext ctx;        
     ctx = new FileSystemXmlApplicationContext("src/main/resources/usuarioe.xml");
        UsuarioService service = (UsuarioService)ctx.getBean( "usuarioService" );
        
        Usuario usuario = new Usuario();
        usuario.setNombre( "diego" );
        usuario.setApellidos( "herrera" );
        usuario.setDireccion( "loria 134" );
        
        service.consultaUsuario( usuario );
        service.borrarUsuario(usuario);
        service.consultaUsuario_alternativo(usuario);
        service.actualizarUsuario(usuario);
        service.procesarInformacion();
        service.ProbarThrowException();
    }

    
}

Nota: podemos notar que crean un objeto, cargado desde el código solo con fines de contar con datos para poder trabajar. Es decir hacemos esto para simular datos.

el resultado final sera por supuesto los mensajes que arroja actualmente cada uno de estos métodos y un error generado desde uno de los métodos a propósito.



















El código del proyecto hasta aquí lo podemos descargar sin problema codigofuente.zip

Ahora lo que les pido es que imaginemos que tenemos grandes implementaciones y una capa data acces layer que consume datos de una base de datos como Oracle o MYSQL que definen un proyecto de gran envergadura. 

Problemática 1: Imaginemos que surge la necesidad de realizar acciones antes de la llamada a un método de una clase, y de acuerdo a los datos de ingresos necesitamos realizar variaciones sobre los mismos o quizás registrar los mismos,  no imagínate que lo necesitas hacer sobre muchos métodos de una clase. De seguro viene un programador piola y te dice no hay problema agregamos código en la capa de servicio del componente y solucionado.

Overtime bienvenido…. En ese momento se le subió el colesterol al equipo, los programadores comienzan a llamar por teléfono avisando que llegan tarde o que no llegan.

Ahora el tema es que vamos a tener que tocar muchos métodos, muchas horas de desarrollo, horas de test y estamos dejando de lado que la responsabilidad del componente no es esa.

Para esto AOP de proporciona una solución con la utilización de un advise before, que posibilita interceptar a un método antes de su procesamiento, posibilitando manipular los datos de ingresos.

Que Gano ¿?  Y primero el poder realizar las acciones que crea conveniente antes del procesamiento como lo necesita mi necesidad, segundo no tener que modificar mi capa de servicio que ya fue recontra testeada, solo me centrare en testear el código de mi intercepsión o acciones que quiera realizar, por ultimo no le daré responsabilidades que no corresponden al método.

Mama o señora esposa gracias a spring AOP voy a llegar para la comida.

Primero vamos a definir el codigo que aplicaremos realizada la intercepción y esto lo haremos creando una clase que implemente MethodBeforeAdvice y trabajando sobre el método before.

Com.company.aop. EventoBeforeAdvice.java

package com.company.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

import com.mycompany.modelo.Usuario;

public class EventoBeforeAdvice implements MethodBeforeAdvice {

 public void before(Method arg0, Object[] arg1, Object arg2)
   throws Throwable {
  System.out.println( "***************** AOP BEFORE ADVICE *********************" );
        Usuario usuario = (Usuario)arg1[0];
        usuario.setNombre( usuario.getNombre() + " MODIFICADO POR AOP" );
        System.out.println( "AOP BEFORE ADVICE : Accediendo al Usuario " + usuario.getNombre() );
        System.out.println( "**********************************************************" );
 }

}


Nota: podemos observar que trabajamos sobre el método before que recibe el método y los parámetros con los cuales podemos trabajar, en el ejemplo realizo una modificación al input que por supuesto es lo que llegara luego al método interceptado antes de su procesamiento.

Ahora todo bien, pero si no le indico a mi spring bean configuration usuarioe.xml de nuestro proyecto como aplicar mi interceptor no vamos a llegar a ningún lado, por eso realizamos cambios sobre el archivo mencionado donde queda asi.

src/main/resources/usuarioe.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   <!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioServiceTarget" class = "com.company.services.UsuarioServiceImpl"/>
   <!-- Definiciono del bean de la implementacion del advise a aplicar--> 
   <bean id = "eventoBeforeAdvice" class = "com.company.aop.EventoBeforeAdvice"/>
   <!-- definimos mi ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo 
   Spring. Deja un bean configurado dentro del contexto, listo para ser usado.  -->
   <bean id = "usuarioService" class = "org.springframework.aop.framework.ProxyFactoryBean">    
        <!-- Definicion la interfaz que tiene el metodo a interceptar-->   
        <property name = "proxyInterfaces">
            <value>com.company.services.UsuarioService</value>
        </property>
        <!--  defino el bean interceptor -->
        <property name = "interceptorNames">
            <list>
                <value>eventoBeforeAdvice</value>
            </list>
        </property>
        <!-- Referencia a un Bean que implementa los metodos a los que vamos aplicar el advice-->
        <property name = "target">
            <ref bean = "usuarioServiceTarget"/>
        </property>
    </bean>
</beans>

Lo primero que hacemos es definir los bean con los que vamos a trabajar para tal propósito.

Primero defino por supuesto el bean que hace referencia a la clase con la implementación del o los métodos a interceptar.

<!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioServiceTarget" class = "com.company.services.UsuarioServiceImpl"/>

Lo siguiente sera definir el bean para  codigo que ejecutaremos cuando realicemos la intercepsion.


<!-- Definiciono del bean de la implementacion del advise a aplicar--> 
   <bean id = "eventoBeforeAdvice" class = "com.company.aop.EventoBeforeAdvice"/>

Por ultima defino el bean que la tiene clara y sabe lo que tiene que hacer.

<!-- definimos mi ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo 
   Spring. Deja un bean configurado dentro del contexto, listo para ser usado.  -->
   <bean id = "usuarioService" class = "org.springframework.aop.framework.ProxyFactoryBean">    
        <!-- Definicion la interfaz que tiene el metodo a interceptar-->   
        <property name = "proxyInterfaces">
            <value>com.company.services.UsuarioService</value>
        </property>
        <!--  defino el bean interceptor -->
        <property name = "interceptorNames">
            <list>
                <value>eventoBeforeAdvice</value>
            </list>
        </property>
        <!-- Referencia a un Bean que implementa los metodos a los que vamos aplicar el advice-->
        <property name = "target">
            <ref bean = "usuarioServiceTarget"/>
        </property>
    </bean>

Nota: tiene que quedar claro que este bean hace referencia a  proxyfactorybean que deja un bean configurado en el contexto listo para ser usado, donde definimos la interfaz de la clase con los métodos a intercetpar , segundo la lista de interceptores  que aplicaremos y por ultimo el bean que contiene la implementación de los métodos a interceptar. Note que cambie el nombre del bean de la clase que implementa los metodos, solo lo realice para que no toquemos nada en nuestra clase test.

Pero analicemos la corrida de estos cambios, que groso es verdad se esta ejecutando el código que puse en mi clase interceptora antes de la llamada de cada uno de los metodos de la clase implementada que mencione y por supuesto puedo ver como va cambiando el input ante de la llamada a los métodos..


Nota: lo mas loco es que intercepte con código los métodos de la clase y pude realizarlo de manera externa. 

Puede descargar el proyecto hasta donde estamos codigofuente.zip

Cambio de alcance: Pero la realidad es que esto no estaría bueno si no me proporcionara cierta flexibilidad en poder determinar que métodos quiero interceptar es decir un filtro para los advice y aquí es donde entra el concepto de point cut. Se necesita interceptar solo los métodos que tengan en su nombre consulta.

Para ello nuestro usuarioe.xml quedaría asi.

src/main/resources/usuarioe.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioServiceTarget" class = "com.company.services.UsuarioServiceImpl"/>
   <!-- Definiciono del bean de la implementacion del advise a aplicar--> 
   <bean id = "eventoBeforeAdvice" class = "com.company.aop.EventoBeforeAdvice"/>
   
   
   <!-- definir el patron para los metodos que seran interceptados por los advice -->
   <bean id = "consultaPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
       <!-- dentro de los patrones puedo definir  -->
        <property name = "pattern">
            <value>.*consulta.*</value>
        </property>        
        <property name = "advice">
            <ref bean = "eventoBeforeAdvice"/>
        </property>
  </bean>
  
   <!-- definimos mi ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo 
   Spring. Deja un bean configurado dentro del contexto, listo para ser usado.  -->
   <bean id = "usuarioService" class = "org.springframework.aop.framework.ProxyFactoryBean">    
        <!-- Definicion la interfaz que tiene el metodo a interceptar-->   
        <property name = "proxyInterfaces">
            <value>com.company.services.UsuarioService</value>
        </property>
        <!--     defino el bean interceptor -->
        <property name = "interceptorNames">
            <list>
                <value>consultaPointCut</value>
            </list>
        </property>
        <!-- Referencia a un Bean que implementa los metodos a los que vamos aplicar el advice-->
        <property name = "target">
            <ref bean = "usuarioServiceTarget"/>
        </property>
    </bean>
    
</beans>

Ahora en nuestro archivo, lo primero que hacemos es definir nuestro bean para la clase UsuarioServiceImpl.java y por supuesto para nuestro interceptor.

<!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioServiceTarget" class = "com.company.services.UsuarioServiceImpl"/>
   <!-- Definiciono del bean de la implementacion del advise a aplicar--> 
   <bean id = "eventoBeforeAdvice" class = "com.company.aop.EventoBeforeAdvice"/>


El siguiente paso sera definir el filtro o pointcut que define el patron para metodos que se aplicara el advice, y por supuesto define el advise.

<!-- definir el patron para los metodos que seran interceptados por los advice -->
   <bean id = "consultaPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
       <!-- dentro de los patrones puedo definir  -->
        <property name = "pattern">
            <value>.*consulta.*</value>
        </property>        
        <property name = "advice">
            <ref bean = "eventoBeforeAdvice"/>
        </property>
  </bean>


Por ultimo la definición del aspecto por medio de ProxyFactoryBean deja listo el bean en el contexto para ser utilizado por la aplicación.

<!-- definimos mi ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo 
   Spring. Deja un bean configurado dentro del contexto, listo para ser usado.  -->
   <bean id = "usuarioService" class = "org.springframework.aop.framework.ProxyFactoryBean">    
        <!-- Definicion la interfaz que tiene el metodo a interceptar-->   
        <property name = "proxyInterfaces">
            <value>com.company.services.UsuarioService</value>
        </property>
        <!--     defino el bean interceptor -->
        <property name = "interceptorNames">
            <list>
                <value>consultaPointCut</value>
            </list>
        </property>
        <!-- Referencia a un Bean que implementa los metodos a los que vamos aplicar el advice-->
        <property name = "target">
            <ref bean = "usuarioServiceTarget"/>
        </property>
    </bean>

El resultado final será que interceptamos con el advice solo las llamadas de los métodos con nombre que contenga el string “consulta”.


Nota final: alcanzamos el objetivo de interceptar los métodos sin tocar el código de las clases del negocio sin ensuciar nuestro código,

El código del proyecto al estado actual se puede realizar desde aqui codigofuente.zip


Problemática 2: utilizaremos el ejemplo anterior para mostrar el funcionamiento de 2 advise disponibles con spring.

El siguiente paso sera abordar los siguientes advice:
  • After Returning :  Es decir interceptar a un método luego de su ejecución y trabajar por supuesto con su output. Para nuestro ejemplo la condición para el advice estará sobre los métodos que contengan en su nombre el string "Actualizar", lo que en nuestro ejemplo significara interceptar la llamada al método Actualizar.
  • Around : Con el buscaremos interceptar un metodo, realizando acciones antes y después de su procesamiento.  Para nuestro ejemplo trabajaremos sobre la llamada al metodo ProcesarInformacion, donde mediante hilos buscamos simular procesamiento para poder observar el comportamiento.
ahora la primera instancia por supuesto sera trabajar sobre la implementacion de los interceptores:

com.company.aop.EventoAfterAdvice.java:  sera la clase que contiene el codigo de la implementacion par advise After Returning que implementa la interfaz AfterReturningAdvice y que nos proporciona el metodo afterReturning el cual claramente nos brindara como parametros de entrada el retorno que realiza el metodo interceptado.

package com.company.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class EventoAfterAdvice implements AfterReturningAdvice {

 public void afterReturning(Object arg0, Method arg1, Object[] arg2,
   Object arg3) throws Throwable {
  System.out.println( "***************** AOP AFTER ADVICE *********************" );
        int records = ((Integer)arg0).intValue();
        if ( records == 1 ) {
            System.out.println( "AOP AFTER ADVICE : Nº Registros actualizados : " + records );
            System.out.println( "MAIL ENVIADO AL ADMINISTRADOR" );
        }
        else {
            System.out.println( "AOP AFTER ADVICE : NO se ha actualizado ningún registro" );
        }

 }

}

Nota: en nuestro ejemplo el método ActualizarUsuario retorna un valor escalar siempre, por lo que mi implementacion tendra como parametro de salida un valor entero. Podemos observar que a nivel codigo no realiza grandes cosas dado que solo intentamos demostrar la flexibilidad para trabajar en base a los datos de salida del metodo interceptado, es decir imprimimos por consola uno o otro mensaje dependiendo del valor de salida. 

com.company.aop.EventoMethodInterceptorAdvice.java:  sera la clase que contiene el codigo de la implementacion para el advise Around. Imagina la posibilidad de agregar una condicion de validacion para la ejecucion de un metodo en base a los valores de ingreso al metodo totalmente desacoplado de responsabilidades para el componente del negocio, este es el caso dado que esta clase implementa la interfaz MethodInterceptor que nos proporciona el metodo invoke donde podemos trabar antes de la ejecucion del metodo, decidir si vamos a ejecutar el metodo , y trabajar posterior a la ejecucion del mismo, pero me diran sos un piola "solo veo como parámetro de entrada el metodo a interceptar", pero esto por que están perdiendo visibilidad, por que los advice se utilizan en conjunto, podriamos con el advice before tener los datos de ingreso y con el after a los datos de salida.

package com.company.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class EventoMethodInterceptorAdvice implements MethodInterceptor {

 public Object invoke(MethodInvocation arg0) throws Throwable {
  // TODO Auto-generated method stub
  System.out.println( "****** INICIO AOP METHOD INTERCEPTOR ADVICE **********" );

        long tiempoInicial = new java.util.Date().getTime();
        Object objeto = arg0.proceed();
        long tiempoEjecucion = ( new java.util.Date().getTime() - tiempoInicial ) / 1000;

        System.out.println( "Nombre del Método : " + arg0.getMethod() );
        System.out.println( "Tiempo de proceso del método ( segundos ) : " + tiempoEjecucion );
        System.out.println( "******* FIN AOP METHOD INTERCEPTOR ADVICE **********" );

        return objeto;
 }

}


Nota: en nuestro ejemplo no hacemos nada loco, solo una demostración de que trabajamos antes y después de la ejecución del método, imprimimos mensajes en consola y calculamos tiempos para demostrar que trabajamos ejecutado el metodo.

ahora nuestro archivo de usuarioe.xml quedaría de la siguiente forma luego de las modificaciones necesaria.  
  
src/main/resources/usuarioe.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- Definicion la implementacion de la interfaz con el metodo a interceptar--> 
   <bean id = "usuarioServiceTarget" class = "com.company.services.UsuarioServiceImpl"/>
   <!-- Definiciono del bean de la implementacion del advise a aplicar--> 
   <bean id = "eventoBeforeAdvice" class = "com.company.aop.EventoBeforeAdvice"/>
   <!-- definimos el bean de la implementacion del advice after -->
   <bean id = "eventoAfterAdvice" class = "com.company.aop.EventoAfterAdvice"/>
   <!-- definimos el bean de la implementacion del advice around -->
   <bean id = "eventoMethodInterceptorAdvice" class = "com.company.aop.EventoMethodInterceptorAdvice"/>
   
   
   <!-- definir el patron para los metodos que seran interceptados por los advice -->
   <bean id = "consultaPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
       <!-- dentro de los patrones puedo definir  -->
        <property name = "pattern">
            <value>.*consulta.*</value>
        </property>        
        <property name = "advice">
            <ref bean = "eventoBeforeAdvice"/>
        </property>
  </bean>
  
  <bean id = "actualizaPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name = "pattern">
            <value>.*actualizar.*</value>
        </property>
        <property name = "advice">
            <ref bean = "eventoAfterAdvice"/>
        </property>
 </bean>
 
     <bean id = "procesaPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name = "pattern">
            <value>.*procesar.*</value>
        </property>
        <property name = "advice">
            <ref bean = "eventoMethodInterceptorAdvice"/>
        </property>
    </bean>
    
   
   
 
  
   <!-- definimos mi ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo 
   Spring. Deja un bean configurado dentro del contexto, listo para ser usado.  -->
   <bean id = "usuarioService" class = "org.springframework.aop.framework.ProxyFactoryBean">    
        <!-- Definicion la interfaz que tiene el metodo a interceptar-->   
        <property name = "proxyInterfaces">
            <value>com.company.services.UsuarioService</value>
        </property>
        <!--     defino el bean interceptor -->
        <property name = "interceptorNames">
            <list>
                <value>consultaPointCut</value>
                <value>actualizaPointCut</value>
                <value>procesaPointCut</value>
            </list>
        </property>
        <!-- Referencia a un Bean que implementa los metodos a los que vamos aplicar el advice-->
        <property name = "target">
            <ref bean = "usuarioServiceTarget"/>
        </property>
    </bean>
    
</beans>



En primera instancia es necesario declarar los beans correspondiente a la implementacion de los advice.


<!-- definimos el bean de la implementacion del advice after -->
   <bean id = "eventoAfterAdvice" class = "com.company.aop.EventoAfterAdvice"/>
   <!-- definimos el bean de la implementacion del advice around -->
   <bean id = "eventoMethodInterceptorAdvice" class = "com.company.aop.EventoMethodInterceptorAdvice"/>

La segunda parte sera la definición de la condicion para que se disparen los interceptores y en nuestro caso tendremos 2.

actualizaPointCut es el pointcut que hace referencia al patron que indica una condicion sobre el metodo actualizar y que por supuesto hace referencia al advice after.


<bean id = "actualizaPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name = "pattern">
            <value>.*actualizar.*</value>
        </property>
        <property name = "advice">
            <ref bean = "eventoAfterAdvice"/>
        </property>
 </bean>

procesaPointCut es el pointcut que hace referencia al patron que indica una condicion sobre el metodo procesar y que por supuesto hace referencia al advice around.


     <bean id = "procesaPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name = "pattern">
            <value>.*procesar.*</value>
        </property>
        <property name = "advice">
            <ref bean = "eventoMethodInterceptorAdvice"/>
        </property>
    </bean>

y por supuesto deberemos modificar la definicion del aspecto generado por ProxyFactoryBean que tiene el bean listo y cargado en el contexto, donde podemos notar claramente que agregamos a la lista de interceptores los 2 nuevos pointcut.


   <!-- definimos mi ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo 
   Spring. Deja un bean configurado dentro del contexto, listo para ser usado.  -->
   <bean id = "usuarioService" class = "org.springframework.aop.framework.ProxyFactoryBean">    
        <!-- Definicion la interfaz que tiene el metodo a interceptar-->   
        <property name = "proxyInterfaces">
            <value>com.company.services.UsuarioService</value>
        </property>
        <!--     defino el bean interceptor -->
        <property name = "interceptorNames">
            <list>
                <value>consultaPointCut</value>
                <value>actualizaPointCut</value>
                <value>procesaPointCut</value>
            </list>
        </property>
        <!-- Referencia a un Bean que implementa los metodos a los que vamos aplicar el advice-->
        <property name = "target">
            <ref bean = "usuarioServiceTarget"/>
        </property>
    </bean>

El resultado se reduce a la demostración de como interceptamos con estos advice la ejecución de los métodos que indicamos con anterioridad.




Nota: como siempre para el que se cuelga de la programación en este momento, jajaja. la excepcion que se dispara esta generada de manera intencional desde el principio dado que la utilizaremos en el siguiente paso.

Pero en cosas a notar debemos observar que interceptamos el método actualizar luego de su ejecución y que interceptamos el método procesar antes de su ejecución y trabajamos luego de su procesamiento, con lo que el objetivo de la demostración llego a su fin.

El código del proyecto hasta el estado actual se puede descargar codigofuente.zip.

Nota Final: dado que no estaría bueno no hablar del advice Throws teniendo en cuenta que trabajamos con código que al final siempre me dio una exeption generada por nuestro a codigo  con este fin es que vamos a tocar un poco el tema.

En primera instancia como los otros debemos generar una implementacion para este interceptor que implementa la interfaz ThrowsAdvice, que nos proporciona un método con el que podemos trabajar afterThrowing, que no brinda la posibilidad de poder trabajar generada una exception desde un metodo que le indicaremos por supuesto desde usuarioe.xml.

com.company.oap.EventoThrowsAdvice.java: la clase a nivel código no tiene gran complicación solo la impresión de un mensaje en consola para demostrar que pasamos por el código realizada la intercepcion. Pero prestar atención en el input del método recibe una excepción, por que dependiendo de la excepción que genere desde mi codigo entrara por aqui, por lo que necesito sobrecargar este metodo para todas las excepciones en las que quiera trabajar en acción.


package com.company.aop;

import org.springframework.aop.ThrowsAdvice;

public class EventoThrowsAdvice implements ThrowsAdvice {
 
 public void afterThrowing(Exception e) throws Throwable {
  System.out.println("Exception capturada : Throw exception capturada y lista para procesamiento!");
 }
 
}


En el unico metodo  ProbarThrowException()  generamos una excepción genérica de tipo exception.


public void ProbarThrowException() throws Exception {
  System.out.println("ProbarThrowException() esta corriendo ");
  throw new Exception("Generic Error");
 }

podemos observar que en la clase antes mencionada la captura por tiene una correspondencia en los tipos de la excepción de input del metodo afterThrowing. si mi ejemplo generara una exception de tipologia asociado a base de datos por ejemplo, de seguro no interceptaria la misma dado que el advice no lo esta soportando.

Nota: sobre su configuración en el archivo mencionado no hablaremos dado que no presenta a esta altura grandes desafios.

defino el bean para la implementacion del advice

<!-- definimos el bean de la implementacion del advice Throws -->

   <bean id="EventoThrowsAdvice" class="com.company.aop.EventoThrowsAdvice" />

defino el pointcut donde indicare por ejemplo que se dispare en todas las excepciones que se generen para la clase de implementacion.

<!-- definir el patron para los metodos que seran interceptados por los advice -->
   <bean id = "ExepcionPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
       <!-- dentro de los patrones puedo definir  -->
        <property name = "pattern">
            <value>.*</value>
        </property>        
        <property name = "advice">
            <ref bean = "EventoThrowsAdvice"/>
        </property>

  </bean>

y por ultimo agregare el pointcut al proxy dinámico que tiene la información de  la interfaz , la lista de interceptores y por supuesto la referencia del bean que hace referencia a la implementacion de la interfaz.

<!-- definimos mi ProxyFactoryBean: Es la clase de Spring Framework para crear proxys dinámicos, al estilo
   Spring. Deja un bean configurado dentro del contexto, listo para ser usado.  -->
   <bean id = "usuarioService" class = "org.springframework.aop.framework.ProxyFactoryBean">  
        <!-- Definicion la interfaz que tiene el metodo a interceptar-->
        <property name = "proxyInterfaces">
            <value>com.company.services.UsuarioService</value>
        </property>
        <!--     defino el bean interceptor -->
        <property name = "interceptorNames">
            <list>
                <value>consultaPointCut</value>
                <value>actualizaPointCut</value>
                <value>procesaPointCut</value>
                <value>ExepcionPointCut</value>
            </list>
        </property>
        <!-- Referencia a un Bean que implementa los metodos a los que vamos aplicar el advice-->
        <property name = "target">
            <ref bean = "usuarioServiceTarget"/>
        </property>
    </bean>

con todo esto el resultado final de la ejecucion demostraría nuestra intercepcion a la excepcion generada por codigo demostrando el paso por el código de la implementacion del advice.




El código final del proyecto se puede descargar desde aqui. codigofuente.zip