PATRÓN DE DISEÑO PROXY
Los
ejemplos de este tutorial están hechos con el siguiente
entorno de desarrollo:
-
Jboss Eclipse
IDE. - JDK 1.5
INTRODUCCIÓN
hace unos años están apareciendo gran cantidad de
herramientas que introducen nuevos conceptos como la orientación
a aspectos (AOP), la interceptación, etc… . En el
fondo, todas ellas están basadas en algo no tan nuevo como es el
patrón Proxy. Podríamos definir un proxy, como un
objeto que fuerza que todas las llamadas al objeto al que se desea
invocar pasen previamente a través de él. Esto nos
permite realizar acciones que podríamos llamar transversales a
la funcionalidad realizada por el objeto final invocado, como por
ejemplo, trazabilidad, seguridad, transaccionalidad etc… . Es decir,
si por ejemplo nosotros invocamos al método sayHello del objeto obj de la clase Saludo,
podríamos «interceptar» esa llamada a través del Proxy,
para comprobar si realmente el que invoca al método tiene
permisos para realizar la acción, sin tener que modificar para
ello el método sayHello .
En esto se basan gran cantidad de herramientas como Spring, Acegi, la
arquitectura de EJBs de gran cantidad de servidores de aplicaciones
como JBoss, etc.
Para ello, usaremos la clase java.lang.reflect.Proxy que lleva ya con nosotros desde la versión 1.3 de la J2SE.
Sin más, vamos a realizar un ejemplo sencillo, para demostrar todo esto:
EL EJEMPLONos
crearemos un proyecto nuevo en nuestro Eclipse, y generaremos primero
un Interfaz que denominaremos Manager, que define toda la funcionalidad
que preveemos puede tener la lógica de negocio de las clases de
nuestra aplicación:
package com.autentia.adictos.proxy; public interface Manager { public void save(Object obj); public void remove(Integer id); }Vamos a construir ahora una clase implemente el interfaz:
package com.autentia.adictos.proxy; public class UserManagerImpl implements Manager { public void save(Object obj) { System.out.println("I save user Objects"); } public void remove(Integer id) { System.out.println("I remove user objects"); } }
Vamos ahora a contruir el Manejador o Handler, que será el que
será previamente invocado a los métodos de los managers:
package com.autentia.adictos.proxy;
package com.autentia.adictos.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ManagerHandler implements InvocationHandler { Manager realManager = null; public ManagerHandler(Manager realManager) { this.realManager = realManager; } /* Este es el método callback que será invocado previamente a cada método de los managers. */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("A Manager is being invoked:"+method.getName()); // Continuamos invocando al Manager real. return method.invoke(realManager,args); } }
Ahora crearemos una Factor�a de Managers, que creará los mismos encapsulados dentro de un Proxy:
package com.autentia.adictos.proxy; import java.lang.reflect.Proxy; public class FactoryManager { public Manager createManager(Class claseManager) { Manager realManager = null; try { // Creamos un objeto de la clase que recibimos. realManager = (Manager)claseManager.newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } /* Creamos el CallBack Handler y le pasamos el objeto real para ser invocado posteriormente en su método invoke. */ ManagerHandler handler = new ManagerHandler(realManager); // Creamos el proxy. Class[] interfacesQueEncapsulo = new Class[] {Manager.class}; return (Manager)Proxy.newProxyInstance( claseManager.getClassLoader(), interfacesQueEncapsulo,handler); } }
Creamos una clase para probar lo que hemos hecho:
package com.autentia.adictos.proxy;
package com.autentia.adictos.proxy; public class Test { public static void main(String[] args) { // Creamos ahora la factoria FactoryManager factory = new FactoryManager(); // Usamos la factoria para crear un Proxy sobre UserManagerImpl Manager manager = factory.createManager(UserManagerImpl.class); Object obj = new Object(); // Invocamos a los métodos: manager.save(obj); manager.remove(1); } }
Mostremos la salida estándar:
podéis ver, el código del Handler es invocado previamente
al código del UserManagerImpl. Curioso ¿no?.
¿Os suena esto ?. Supongo que si conocías ya el
tema de AOP, AspectJ etc…, verás que se parece
sospechosamente. Ahora, en el manejador podríamos trazar lo que
ocurre, o guardar en base de datos un registro, o autenticar o
autorizar, o abrir una transacción, invocar al método y
cerrar una transacción, etc… Suponed éste
código:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { openTransaction(); Object obj = method.invoke(realManager,args); closeTransaction(); return obj; }Creo que esto habla por sí s�lo.
El único problema es que el hacer que nuestros Managers
implementen un interfaz es algo rígido, por eso es mejor usar
frameworks que nos proporcionan todo esto de una manera más
flexible, como Spring, Acegi, EJB 3.0 etc…, pero creo que es
más bonito conocer como se hacen las herramientas o en que se
basan antes de usarlas, y no creer que es magia….
Excelente ejemplo, muchas gracias. Realmente no está tan obvio, pero tampoco no está difícil nada que no arregle un hora de debuggear.