martes, 7 de septiembre de 2010

Los proxies en Java, Dynamic Proxies vs CGlib, y su uso desde Spring

Empiezo con este artículo a comentar cosas que normalmente llaman la atención cuando las nombro en clase, son temas que cuando se pronuncian el mundo asiente, pero que sinceramente creo casi todos flojeamos con ellas. En esta línea de artículos pienso hablar de conceptos que muchas veces yo doy por sabidos en los cursos. Así podré referenciar a mi propio blog para explicar aspectos que muchas veces no caben en los temarios, ¡jejeje! (malévolo).

Además este es el primer artículo técnico que escribo, los anteriores (y de los que habrá más) han sido de opinión/valoración.


Los Proxies

Todos los días los programadores Java usamos proxies de manera consciente o inconsciente: por ejemplo, en las llamadas remotas vía RMI, o con los componentes EJB, o con los servicios web vía JAX-RPC, incluso cuando usamos HibernateJPA, o en ocasiones Spring AOP, estamos haciendo uso de clases proxy, estos proxies nos hacen el "trabajo sucio" para que nuestro código no tenga que ocuparse de tales tareas, es decir se encargan de serializar-deserializar objetos, abrir-cerrar transacciones, etc.

Pero, ¿Qué es un Proxy exactamente?

Las clases proxy son como
 los dobles de cine.
Una clase Proxy o Delegada es simplemente una clase que implementa los métodos de otra, como si fuera un doble de cine, que asume responsabilidades en nombre de otra clase. Un proxy permite implementar multitud de patrones de diseño de una aplicación de forma más natural y sencilla. Realmente lo que creamos es un objeto doble de otro objeto en memoria.

Vale,  ¿Cómo se construyen?

Normalmente los entornos de desarrollo nos permiten crear clases nuevas que tengan los mismos métodos que otra dada y realizar la retrollamada automática o callback, por ejemplo, en eclipse esto se consigue con el menú Source/Generate Delegate Methods de la perspectiva Java.

Veamos un ejemplo:


Creo que el código anterior no necesita explicación ninguna...

¿Y no se puede hacer mejor?

Sí, ciertamente crear el código fuente de la clase del Proxy "a mano" con o sin ayuda de un IDE es un poco molesto y poco productivo, aunque precisamente eso era lo que hacíamos cuando usábamos herramientas  como el compilador rmic de java hasta la versión 1.4 o al desplegar un módulo ejb-jar 2.x.

Existen dos alternativas para no tener que hacer este trabajo por adelantado en nuestro código fuente: la clases para crear Proxies Dinámicos del paquete Reflection (java.lang.reflect) y la librería para generación de código CGlib:


Dynamic Proxies

A partir de JSE 1.3 incluido en el API de Reflection, Java trae la funcionalidad de crear Dynamic Proxies, es decir clases que se generan dinámicamente para suplantar/modificar/ampliar el comportamiento de otras, veamos el código:


Aquí si tenemos cosas que comentar:

1) Para crear un doble (un proxy dinámico) sólo necesitamos llamar a la función Proxy.newProxyInstance, esta función recibe 3 parámetros:
  • el cargador de clases de la clase de la que queremos hacer el proxy.
  • un array con la interfaz o interfaces que contienen los métodos que queremos interceptar de la clase actor.
  • el objeto que recibirá la retrollamada por parte del proxy, en nuestro caso el doble.

¡Et voilà! automáticamente se invocará la función InvocationHandler.invoke de nuestro objeto doble con las llamadas a las funciones del las interfaces introducidas como segundo parámetro de función newProxyInstance.

Esto permite poder construir clases proxies de cualquier clase que implemente al menos una interfaz. pero ¿y si nuestra clase actor no implementará ninguna interfaz...? entonces tendremos que recurrir a una librería llamada cglib.


CGLib

Este framework realmente es una librería de macros de ASM, que es una librería de manipulación de "bytecode", es decir, de código máquina java. Dentro de la CGLib se pueden encontrar (aunque muy mal documentadas) diversas funciones de utilidad, en nuestro caso vamos a usar una clase llamada net.sf.cglib.proxy.Enhancer 

Veamos el código:


Inspeccionando el código, descubrimos que no hace falta ninguna interfaz para usar la clase Enhancer sólo la programamos para funcionar con los métodos setSuperclass setCallback. Bastante fácil, ¿no?, no es de extrañar que Hibernate e iBatis estén escritos con esta librería de proxies. ¿Cual es mejor? unos hablan del rendimiento algo más lento de los proxies dinámicos, otros hablan de las dependencías con librerías (jars) que impone la CGLib. 

Para mi la diferencia entre ambas es algo más sutil, con los proxies dinámicos el proxy se crea partiendo de una instancia de una clase, con la CGlib el proxy se crea a partir de una clase no de una instancia, de forma que para crear un proxy de un objeto concreto con la cglib hay que copiar los valores de los atributos del objeto molde después de crear el proxy, esto suena a Commons BeanUtils, una de las dependencias de Hibernate...


Spring ProxyFactoryBean

Para acabar este "ladrillo" diré que si usamos Springframework, la creación de proxies se ha automatizado con una clase llamada org.springframework.aop.framework.ProxyFactoryBean que permite crear e inyectar dependencias a nuestro código de objetos que sean proxies creados tanto  con el api de reflection  como con la cglib controlando el propiedad proxyTargetClass de dicha clase, aunque en las últimas versiones de Spring la propia existencia o no de una interfaz a la hora de crear el proxy es lo que determina por defecto el comportamiento de esta clase.

Veamos el ejemplo como colofón:


Espero haber aclarado algo a alguien a estas alturas del post sobre el fantástico mundo de los proxies en Java.

¡Hasta la próxima!