viernes, 5 de febrero de 2010

Creación de Aspectos con Spring y XML

En los dos post anteriores vimos como podemos crear aspectos mediante anotaciones y casi nada de xml más que un simple tag ahora vamos a seguir con los aspectos pero ya no con anotaciones sino con XML, los aspectos al no tener anotaciones que los configuren como aspectos toman al forma de simple java beans:

AuditoriaAspectXml.java
package pe.com.slcsccy.aop;
import pe.com.slcsccy.bean.Libro;
public class AuditoriaAspectXml {
public void auditar(Libro libroin, Object instancia) {
System.out.println("Aplicando Aspecto de auditoria auditar(..) @Before! con instancia:"+instancia.getClass().getCanonicalName());
}
}

ImpresorAspectXml.java
package pe.com.slcsccy.aop;
import pe.com.slcsccy.bean.Libro;
public class ImpresorAspectXml {
public void asignarAutor(Libro libroin, Object instancia) {
if(libroin!=null)libroin.setAutor("Carlos Cacique");
System.out.println("Aplicando Aspecto asignarAutor(..) @Before! con instancia:"+instancia.getClass().getCanonicalName());
}
}


La prueba de los aspectos se dispuso de tal forma que AuditoriaAspectXml.java se ejecute primero y antes del aspecto ImpresorAspectXml.java y que al final se ejecute la lógica de negocio.
Es decir el orden de ejecución debería ser el siguiente:

1. AuditoriaAspectXml.auditar();
2. ImpresorAspectXml.asignarAutor();
3. ImpresorService.imprimir()

La configuración mediante spring que logra lo anterior, es la siguiente:

<!-- Le indicamos a spring para que haga uso de las anotaciones @Autowired -->
<context:annotation-config/>
<!-- Le indicamos a spring que lea todas las clases bajo el paquete pe.com.slcsccy.service y sus
subpaquetes y clases, y si encuentra anotaciones estereotipadas(@Service,@Repository,@Controller)
a nivel de clase entonces cree beans del tipo singleton instanciandolas y disponiendolas al proyecto -->
<context:component-scan base-package="pe.com.slcsccy.service"/>
<!-- Creamos javabeans que nos servirá como aspecto -->
<bean id="impresorAspectXml" class="pe.com.slcsccy.aop.ImpresorAspectXml"/>
<bean id="auditoriaAspectXml" class="pe.com.slcsccy.aop.AuditoriaAspectXml"/>
<!-- Definición y configuración de los aspector propiamente dichos -->
<aop:config>
<!-- Configuración del primer aspecto de impresión y de orden=2, el atributo orden indica la secuencia
de ejecución de este aspecto cuando hay un advice que dispara varios aspectos, recuerda que un advice
es la clase en ejecución a la que le aplicamos los aspectos, en este proyecto es el
bean impresorService o instancias de la clase ImpresorServicePdf.java ó ImpresorServiceExcel.java-->
<aop:aspect ref="impresorAspectXml" order="2">
<!-- pointcut=literalmente punto de corte, indica a qué clase se le va a aplicar el aspecto, es
definido mediante expresiones regulares-->
<aop:pointcut id="pointcutImpresor"
expression="execution(public * pe.com.slcsccy.service..*(..)) and args(libroin) and target(instancia)"/>
<!-- aspecto, indica el nombre del método a ejecutar y el pointcut de referencia -->
<aop:before method="asignarAutor" pointcut-ref="pointcutImpresor" />
</aop:aspect>
<!-- Configuración del primer aspecto de impresión y de orden=1 -->
<aop:aspect ref="auditoriaAspectXml" order="1">
<aop:pointcut id="pointcutAuditoria"
expression="execution(public * pe.com.slcsccy.service..*(..)) and args(libroin) and target(instancia)" />
<aop:before method="auditar" pointcut-ref="pointcutAuditoria" />
</aop:aspect>
</aop:config>

El proyecto completo te lo puedes bajar desde AQUI:

lunes, 1 de febrero de 2010

Creación de Aspectos con Spring y Anotaciones Parte 2

Ahora vamos a ver lo siguiente: como podemos ordenar la ejecución de los aspectos cuando estos aspectos se disparan ante el mismo advisor o método. En el post anterior definí un método imprimir de la interface ImpresorService, entonces, ¿qué es lo que pasa si tenemos más de un aspecto declarado ante la ejecución del método imprimir? ¿cuál aspecto se ejecuta primero?¿cómo podemos declarar que un aspecto se ejecute antes que otro?.

Veamos el siguiente requerimiento:
1. Agregar un proceso de auditoria que capture todas las ejecuciones del método imprimir

El requerimiento anterior lo podemos solucionar agregando un aspecto más y definir e implementar tanto la clase como el método de auditoria con la anotación @Order

Solución:
Declaración de componentes de spring:
<context:annotation-config/>
<context:component-scan base-package="pe.com.slcsccy.service"/>
<bean id="impresorAspect" class="pe.com.slcsccy.aop.ImpresorAspect"/>
<bean id="auditoriaAspect" class="pe.com.slcsccy.aop.AuditoriaAspect"/>
<aop:aspectj-autoproxy/>

Implementación de componentes java:
AuditoriaAspect.java:
package pe.com.slcsccy.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import pe.com.slcsccy.bean.Libro;
@Order(1)
@Aspect
public class AuditoriaAspect {
@Order(1)
@Before("execution(public * pe.com.slcsccy.service..*(..)) && "+
"args(libroin) && target(instancia)")
public void auditar(Libro libroin, Object instancia) {
System.out.println("Aplicando Aspecto de auditoria auditar(..) @Before! con instancia:"+instancia.getClass().getCanonicalName());
}
}

ImpresorAspect.java
package pe.com.slcsccy.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import pe.com.slcsccy.bean.Libro;
@Order(2)
@Aspect
public class ImpresorAspect {
@Order(2)
@Before("execution(public * pe.com.slcsccy.service..*(..)) && "+
"args(libroin) && target(instancia)")
public void asignarAutor(Libro libroin, Object instancia) {
if(libroin!=null)libroin.setAutor("Carlos Cacique");
System.out.println("Aplicando Aspecto asignarAutor(..) @Before! con instancia:"+instancia.getClass().getCanonicalName());
}
}

Algunas acotaciones: La anotación @Order debe ser colocada tanto a nivel de clase como a nivel de método, si lo colocamos solo a nivel de método, entonces el orden de ejecución va a estar indicado por el orden en la carga de los beans:
<bean id="impresorAspect" class="pe.com.slcsccy.aop.ImpresorAspect"/>
<bean id="auditoriaAspect" class="pe.com.slcsccy.aop.AuditoriaAspect"/>

domingo, 31 de enero de 2010

Creación de Aspectos con Spring y Anotaciones Parte 1

Hola denuevo, hace mucho tiempo que no posteo y esta vez quiero seguir con los aspectos en Spring, en este post supongo que ya conocen que es un aspecto, para resumirles y hablando como pragramador es un método java que se ejecuta antes, después o antes y después de la ejecución de otro método java, esto nos permite agregar lógica a nuestra aplicación conociendo únicamente la especificación de los métodos a los cuales se les quiere aplicar un aspecto.
La aplicación de ejemplo que he desarrollado es simple: un servicio impresor de libros que tiene un método imprimir con un parámetro Libro a este método imprimir es al cual le aplico una serie de aspectos para agregar las propiedades del libro(autor, isbn y título), los anotaciones que he usado para aplicar los aspectos son: @Aspect, @Before, @After, @AfterReturning, @AfterThrowing, @Around

La clase principal es la siguiente:

package pe.com.slcsccy.aop;

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.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import pe.com.slcsccy.bean.Libro;
import pe.com.slcsccy.service.ImpresorService;

@Aspect
public class ImpresorAspect {

/**
* Este método se ejecuta SIEMPRE antes de cualquier ejecución
* de cualquier método de cualquier clase del paquete pe.com.slcsccy.service y
* que tenga un primer parámetro con nombre libroin
* Obtengo el objeto que viene como parametro y le coloco el nombre del autor,
* además capturo la instancia que disparó el aspecto.
* */
@Before("execution(public * pe.com.slcsccy.service..*(..)) && "+
"args(libroin) && target(instancia)")
public void asignarAutor(Libro libroin, Object instancia) {
if(libroin!=null)libroin.setAutor("Carlos Cacique");
System.out.println("Aplicando Aspecto asignarAutor(..) @Before! con instancia:"+instancia.getClass().getCanonicalName());
}
/**
* Este método se ejecuta SIEMPRE antes de la ejecución de cualquier
* método imprimir que tenga un primer parámetro con nombre libroin de
* cualquier clase que implementa la interface
* pe.com.slcsccy.service.ImpresorService
* */
@Before(value="execution(public * pe.com.slcsccy.service.ImpresorService.imprimir(..)) && "+
"args(libroin) && target(instancia)")
public void asignarISBN(Libro libroin, ImpresorService instancia) {
if(libroin!=null)libroin.setIsbn("41547848");
System.out.println("Aplicando Aspecto asignarISBN(..) @Before! con instancia:"+instancia.getClass().getCanonicalName());
}
/**
* Este método se ejecuta SOLO después de la ejecución EXITOSA de cualquier
* método imprimir que tenga un primer parámetro con nombre libroin
* de cualquier clase que implementa la interface
* pe.com.slcsccy.service.ImpresorService
* */
@AfterReturning(value="execution(public * pe.com.slcsccy.service.ImpresorService.imprimir(..)) && "+
"args(libroin) && target(instancia)",returning="libroOut")
public void asignarTitulo(Libro libroin, Libro libroOut, ImpresorService instancia) {
libroOut.setTitulo("Ejemplo de aspectos con spring");
System.out.println("Aplicando Aspecto asignarTitulo(..) @AfterReturning! con instancia:"+instancia.getClass().getCanonicalName());
System.out.println("Libro Out:"+libroOut);
}
/**
* Este método se ejecuta SIEMPRE después de cualquier
* método imprimir que tenga un primer parámetro con nombre libroin
* de cualquier clase que implementa la interface
* pe.com.slcsccy.service.ImpresorService
* */
@After(value="execution(public * pe.com.slcsccy.service.ImpresorService.imprimir(..)) && "+
"args(libroin) && target(instancia)")
public void finalizarNormal(Libro libroin, ImpresorService instancia) {
System.out.println("Aplicando Aspecto finalizarNormal(..) @After! con instancia:"+instancia.getClass().getCanonicalName());
System.out.println("Libro:"+libroin);
}
/**
* Este método se ejecuta SIEMPRE que ocurre una excepción después de ejecutar
* cualquier método imprimir que tenga un primer parámetro con nombre libroin
* de cualquier clase que implementa la interface
* pe.com.slcsccy.service.ImpresorService
* */
@AfterThrowing(value="execution(public * pe.com.slcsccy.service.ImpresorService.imprimir(..)) && " +
"args(libroin) && target(instancia)",
throwing="excepcion")
public void finalizarConExcepcion(Libro libroin, ImpresorService instancia, ImpresorException excepcion) {
System.out.println("Aplicando Aspecto finalizarConExcepcion(..) @AfterThrowing!");
System.out.println("Mensaje de la Excepcion en el aspecto @AfterThrowing:"+excepcion.getMessage());
}
/**
* Este método se ejecuta dos fases, la primera antes de la ejecución del método y la segunda * cuando el método a terminado satisfactoriamente.
* @throws Throwable
* */
@Around(value="execution(public * pe.com.slcsccy.service.ImpresorService.imprimir(..)) && " +
"args(libroin) && target(instancia)")
public void logging(ProceedingJoinPoint ejecutor,Libro libroin, ImpresorService instancia) throws Throwable {
System.out.println("Iniciando loggin(..) @Around!");
Object retorno = ejecutor.proceed();
System.out.println("Finalizando loggin(..) @Around!, con retorno:"+retorno.getClass().getCanonicalName());
}
}

La aplicación completa en eclipse la puedes descargar desde AQUI.