<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ include file="/WEB-INF/jsp/include.jsp" %> Programación Orientada a Aspectos

Spring AOP

Ramiro Lago Bagüés (Marzo 2008)


Nota previa

Los siguientes ejemplos de código siguen el ejemplo anterior basado en un objeto destino (target object) denominado RecursoGeneral, que implementa el interface Recurso.


Filtrado de métodos y puntos de corte

Un aspecto establece joint points (puntos de unión), que son los puntos de ejecución del objeto destino (por ejemplo, GestorRecursos.liberar()) que entran dentro del aspecto. O dicho de otro modo, los joint point son los puntos de ejecución del objeto destino que producen una invocación a un Advice.

En nuestro caso anterior todas las invocaciones al proxy se derivan en invocaciones al objeto destino, que a su vez acaban en invocaciones al Advice. Este es el comportamiento por defecto: todos los puntos de ejecución del objeto destino son interceptados por el Advice. Pero podemos filtrarlos.

Un punto de corte no es más que una etiqueta o predicado que asocia (match):

Un jointcut no es mas que una etiqueta que establece una relación entre puntos de ejecución del objeto destino y un Advice. Por tanto con un punto de corte somos selectivos, podemos escoger los puntos de ejecución en el objeto destino y su correspondiente Advice.

En el ejemplo podriamos cambiar nuestro application-context para que tuviese un punto de corte llamado controladorAsignacion. Que está proyectado (matched) contra el método asignar() del objeto destino, excluyendo el método liberar(). El nuevo application-context sería:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <bean id="proxyGestor" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
        	<bean class="org.ejemplo.GestorRecursos">
        	</bean>
        </property>
        <property name="interceptorNames">
        	<list>
        		<idref bean="controladorAsignacion" />
        	</list>
        </property>
    </bean>

	<bean id="controladorAsignacion" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
		<property name="mappedName" value="asignar"/>
		<property name="advice" ref="controlador"/>
	</bean>

    <bean id="controlador" class="org.ejemplo.ControlOperaciones">
    </bean>
</beans>

NameMatchMethodPointcutAdvisor es un tipo de punto de corte basado en el nombre de los métodos. Usa una notación parecida al estilo Ant. Por ejemplo los patrones (mappedNames) *Recurso y set* encajan con el método setRecurso. Esto hace que el cliente (main) tenga un comportamiento diferente:


Around Advice

Around advice: también conocido como método interceptor, recibe el control tanto antes como después del punto de ejecución (o joint point, por ejemplo nuestro método asignar()).

Para implementar un around advice no hay más que modificar el appplication-context.xml e implementar el interface MethodInterceptor:


	public interface MethodInterceptor {
		public Object invoke( MethodInvocation invocacion) throws Throwable;
	}

La secuencia del ejemplo es:

  1. El cliente invoca al proxy Spring (como antes con afterReturning).


  2. El proxy Spring llama al invoke() del Advice (en nuestro ejemplo es ControlOperacionesAround). Como argumento pasa una forma o método de invocación (una clase que representa la forma en que se llemará al objeto destino o target object).


  3. El método invoke() del Advice debe llamar o invocar al objeto destino con:
    
    			invocation.proceed();
    		
    Donde invocation es el argumento recibido por el advice, el modo de invocación. proceed() acaba en una llamada al objeto destino, concretamente al método señalado en el application-context. proceed() retorna un Object, que es el objeto retornado por el objeto destino. Ejemplo:
  4. 
    			... antes de llamar al objeto destino ...
    			try {
    				return invocacion.proceed(); // Llamada al objeto destino (joint point)
    			}
    			finally {
    				System.out.println( "Ya se ha ejecutado el Joint Point");
    			}
    		
  5. El Advice realiza en finally las operaciones posteriores a la invocación al objeto destino.


  6. El Advice devuelve el Object al proxy


  7. El proxy devuelve el control al cliente

En el application-context tenemos dos advices, uno para el Joint Point liberar() y otro para el Joint Point asignar(). El advice de liberar() es un Around Advice, que implementa MethodInterceptor:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

    <bean id="proxyGestor" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
        	<bean class="org.ejemplo.GestorRecursos">
        	</bean>
        </property>
        <property name="interceptorNames">
        	<list>
        		<idref bean="controladorAsignacion" />
        		<idref bean="controladorLiberacion" />
        	</list>
        </property>
    </bean>

    <bean id="controladorAsignacion" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="mappedName" value="asignar"/>
        <property name="advice" ref="controlador1"/>
    </bean>
    <bean id="controlador1" class="org.ejemplo.ControlOperaciones">
    </bean>

    <bean id="controladorLiberacion" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    	<property name="mappedName" value="liberar"/>
    	<property name="advice" ref="controlador2"/>
    </bean>
    <bean id="controlador2" class="org.ejemplo.ControlOperacionesAround">
    </bean>

</beans>

Implementando el MethodInterceptor (Around Advice)


public class ControlOperacionesAround  implements MethodInterceptor {
	public Object invoke( MethodInvocation invocacion) throws Throwable {

		System.out.println( "Estamos en " + this.getClass().getName() + ". En invoke()" );
		System.out.println( "   Joint Point (clase interceptada): " +
				invocacion.getThis().getClass().getName() );
		System.out.println( "   Clase que representa la invocación: " +
				invocacion.getClass().getName() );
		System.out.println( "   Método interceptado: " + invocacion.getMethod().getName() );
		System.out.print( "   Argumentos: " );
		for ( Object obj: invocacion.getArguments() )
			System.out.print( obj.toString() + "("+ obj.getClass().getName()+") ");
		System.out.println( "");

		try {
			return invocacion.proceed(); // Llamada al objeto destino (joint point)
		}
		finally {
			System.out.println( "Ya se ha ejecutado el Joint Point");
		}
	}
}

Salida por pantalla con el MethodInterceptor (Around Advice)


Estamos en org.ejemplo.ControlOperacionesAround. En invoke()
   Joint Point (clase interceptada): org.ejemplo.GestorRecursos
   Clase que representa la invocación: org.springframework.aop.framework.ReflectiveMethodInvocation
   Método interceptado: liberar
   Argumentos: Paquete x09(org.ejemplo.RecursoGeneral) Camión 892(org.ejemplo.RecursoGeneral) 
Estamos en org.ejemplo.GestorRecursos y libero Paquete x09 de Camión 892
Ya se ha ejecutado el Joint Point


Volver