Alguna vez habrá visto u oído la Programación Orientada Aspectos (AOP) , pero ¿qué es realmente? Simplemente, es una ayuda para los programadores que permite reducir código de rutinas que siempre deberían ejecutarse y no se puede usar la herencia. Además, cada lógica de negocio solo tendrá lógica de negocio, y no código adicional que son repetitivas y no son parte del negocio. Por ejemplo, un método que se encargue de una transferencia de dinero, debería ser tan simple como esto
void transfer(Account fromAccount, Account toAccount, int amount){
if (fromAccount.getBalance() < 0 ) {
throw new InsufficientFundsException();
}
fromAccount.withdraw(amount);
toAccount.deposit(amount);
}
Pero realmente, se vuelve así:
void transfer(Account fromAccount, Account toAccount, int amount) throws Exception {
if (!getCurrentUser().canPerform(OP_TRANSFER)) {
throw new SecurityException();
}
if (amount < 0) {
throw new NegativeTransferException();
}
Transaction tx = database.newTransaction();
try {
if (fromAccount.getBalance() < amount) {
throw new InsufficientFundsException();
}
fromAccount.withdraw(amount);
toAccount.deposit(amount);
tx.commit();
systemLog.logOperation(OP_TRANSFER, fromAccount, toAccount, amount);
}catch(Exception e) {
tx.rollback();
throw e;
}
}
Donde se incorpora rutinas de registro (log), inicio de transacciones de la base de datos, validación de cantidades, además de posibles excepciones que se puedan lanzar.
Y si hacemos un método que se encargue únicamente del depósito, el resultado sería similar. Aquí es donde entra los Aspectos.
![]() |
Luego, crearemos la interfaz Conversacion, que tendrá un método llamado iniciarSesion().
package springproxy;
public interface Conversacion {
void iniciarSesion();
}
Y tenemos la siguiente implementación de la Interfaz
package springproxy;
public class ChatInterno implements Conversacion{
private String servidor;
public void setServidor(String servidor) {
this.servidor = servidor;
}
public void iniciarSesion() {
System.out.println("["+this.getClass().getName()+":"+this.toString()+"]");
System.out.println("Iniciando sesion del servidor "+servidor);
}
}
Ahora, crearemos un archivo .xml para Spring. Entramos a File > New File, y seleccionamos Other | Spring XML Configuration File.
![]() |
Lo llamaremos "aop-spring", y lo colocaremos en la carpeta src del proyecto.
![]() |
Clic en "Next". No seleccionamos nada en la última página, y hacemos clic en "Finish". Notar que solo se está llamando al esquema "beans".
Escribimos el siguiente código en el archivo aop-spring.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-2.5.xsd">
<bean id="chatInterno" class="springproxy.ChatInterno">
<property name="servidor" value="Servidor Spring Framework"/>
</bean>
<bean id="chatProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="springproxy.Conversacion" />
<property name="target" ref="chatInterno"/>
</bean>
</beans>
Notemos cómo NB 6.1 nos ayuda a escribir el código, llenando los atributos, asignando variables de beans ya creados, etc.
Ahora, escribamos el siguiente código de nuestra clase Main.
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("aop-spring.xml");
Conversacion chat = (Conversacion) ctx.getBean("chatInterno");
chat.iniciarSesion();
System.out.println("corrido de " + chat.getClass());
System.out.println();
Conversacion proxy = (Conversacion) ctx.getBean("chatProxy");
proxy.iniciarSesion();
System.out.println("corrido de " + proxy.getClass());
}
Ejecutemos el proyecto y veamos la salida:
[springproxy.ChatInterno:springproxy.ChatInterno@633e5e] Iniciando sesion del servidor Servidor Spring Framework corrido de class springproxy.ChatInterno [springproxy.ChatInterno:springproxy.ChatInterno@633e5e] Iniciando sesion del servidor Servidor Spring Framework corrido de class $Proxy0
Vemos que se trata del mismo objeto (vemos el ID ChatInterno@633e5e en ambos casos) pero realmente son dos clases diferentes. El primero es de la clase springproxy.ChatInterno y el segundo es de $Proxy0. El Proxy contiene al objeto de ChatInterno, y cuando se ejecuta los métodos del Proxy, llama al método del objeto contenido. El objeto de ChatInterno no sabrá de donde fue llamado. El Proxy podría realizar un código antes o después de llamar al método del objeto ChatInterno. Esto es lo que queremos hacer al usar AOP.
package springproxy;
import java.lang.reflect.Method;
import java.util.Date;
import org.springframework.aop.MethodBeforeAdvice;
public class ChatLogger implements MethodBeforeAdvice{
public void before(Method metodo, Object[] args, Object objetivo) throws Throwable {
System.out.println("["+(new Date())+"] "+metodo.getName()+" llamado desde "+objetivo);
}
}
Ahora, lo instanciemos en el Spring
<bean id="chatLogger" class="springproxy.ChatLogger" />
y lo agreguemos en la lista de interceptores
<bean id="chatProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="springproxy.Conversacion" />
<property name="target" ref="chatInterno"/>
<property name="interceptorNames">
<list>
<value>chatLogger</value>
</list>
</property>
</bean>
Ejecutemos el proyecto, y veremos lo que pasa:
[springproxy.ChatInterno:springproxy.ChatInterno@17494c8] Iniciando sesion del servidor Servidor Spring Framework corrido de class springproxy.ChatInterno [Tue Jun 03 00:52:34 PET 2008] iniciarSesion llamado desde springproxy.ChatInterno@17494c8 [springproxy.ChatInterno:springproxy.ChatInterno@17494c8] Iniciando sesion del servidor Servidor Spring Framework corrido de class $Proxy0
Como ves, no hemos modificado nada de la clase ChatInterno. En la clase ChatLogger podemos agregar el código que deseemos, sin modificar la funcionalidad de nuestra lógica de negocio.
El proyecto de este post se encuentra aquí: http://diesil-java.googlecode.com/files/SpringProxy.tar.gz
| Pantallazo-New.png | ![]() |
49314 bytes |
| Pantallazo-New1.png | ![]() |
36066 bytes |
| Pantallazo-Project.png | ![]() |
43877 bytes |