java reflection cglib dynamic-proxy

java - ¿Cuál es la diferencia entre el proxy dinámico JDK y CGLib?



reflection dynamic-proxy (4)

JDK El proxy dinámico solo puede realizar un proxy por interfaz (por lo que su clase objetivo debe implementar una interfaz, que luego también implementa la clase proxy).

CGLIB (y javassist) pueden crear un proxy mediante subclases. En este escenario, el proxy se convierte en una subclase de la clase objetivo. Sin necesidad de interfaces

Por lo tanto, los proxys dinámicos de Java pueden public class Foo implements iFoo proxy: public class Foo implements iFoo donde CGLIB puede usar proxy: public class Foo

EDITAR:

Debo mencionar que, dado que javassist y CGLIB usan el proxy mediante subclases, esta es la razón por la que no se pueden declarar los métodos finales o hacer que la clase sea definitiva cuando se utilizan marcos que dependen de esto. Eso evitaría que estas bibliotecas dejaran subclase a su clase y anularan sus métodos.

En el caso del patrón de diseño de proxy , ¿cuál es la diferencia entre el Proxy dinámico de JDK y las API de generación de código dinámico de terceros, como CGLib ?

¿Cuál es la diferencia entre usar ambos enfoques y cuándo se debe preferir uno sobre otro?


De la documentación de Spring :

Spring AOP utiliza proxies dinámicos JDK o CGLIB para crear el proxy para un objeto objetivo dado. (Los proxies dinámicos JDK son preferidos siempre que tenga una opción).

Si el objeto de destino a ser procesado implementa al menos una interfaz, entonces se usará un proxy dinámico JDK. Todas las interfaces implementadas por el tipo de objetivo serán procesadas. Si el objeto objetivo no implementa ninguna interfaz, se creará un proxy CGLIB.

Si desea forzar el uso de proxys CGLIB (por ejemplo, para proxy de cada método definido para el objeto de destino, no solo aquellos implementados por sus interfaces) puede hacerlo. Sin embargo, hay algunos problemas a considerar:

los métodos finales no pueden ser advertidos, ya que no pueden ser anulados.

Necesitará los binarios de CGLIB 2 en su classpath, mientras que los proxys dinámicos están disponibles con el JDK. Spring le advertirá automáticamente cuando necesite CGLIB y las clases de biblioteca CGLIB no se encontrarán en la ruta de clase.

El constructor de su objeto proxy se llamará dos veces. Esta es una consecuencia natural del modelo de proxy CGLIB mediante el cual se genera una subclase para cada objeto con proxy. Para cada instancia de proxy, se crean dos objetos: el objeto proxy real y una instancia de la subclase que implementa el consejo. Este comportamiento no se muestra cuando se utilizan proxys JDK. Por lo general, llamar al constructor del tipo de proxy dos veces no es un problema, ya que normalmente solo se llevan a cabo asignaciones y no se implementa una lógica real en el constructor.


Diferencias en la funcionalidad

  • Los proxies JDK permiten implementar cualquier conjunto de interfaces mientras se subclasifica java.lang.reflect.Proxy . Cualquier método de interfaz, más Object::hashCode , Object::equals y Object::toString se reenvía a un InvocationHandler .

  • cglib le permite implementar cualquier conjunto de interfaces al subclasificar cualquier clase que no sea final. Además, los métodos pueden anularse opcionalmente, es decir, no es necesario interceptar todos los métodos no abstractos. Además, hay diferentes formas de implementar un método. También ofrece una clase InvocationHandler (en un paquete diferente), pero también permite llamar a los súper métodos mediante el uso de interceptores más avanzados como, por ejemplo, un MethodInterceptor . Además, cglib puede mejorar el rendimiento mediante intercepciones especializadas como FixedValue . Una vez escribí un resumen de diferentes interceptores para cglib .

Diferencias de rendimiento

Los proxys JDK se implementan ingenuamente con solo un despachador de interceptación, InvocationHandler . Esto requiere un envío de método virtual a una implementación que no siempre puede estar en línea. Cglib permite crear código de bytes especializado, lo que a veces puede mejorar el rendimiento. Aquí hay algunas comparaciones para implementar una interfaz con 18 métodos stub:

cglib JDK proxy creation 804.000 (1.899) 973.650 (1.624) invocation 0.002 (0.000) 0.005 (0.000)

El tiempo se observa en nanosegundos con desviación estándar en llaves. Puede encontrar más detalles sobre el punto de referencia en el tutorial de Byte Buddy, donde Byte Buddy es una alternativa más moderna a cglib. Además, tenga en cuenta que cglib ya no se encuentra en desarrollo activo.


Proxy dinámico: implementaciones dinámicas de interfaces en tiempo de ejecución utilizando JDK Reflection API .

Ejemplo: Spring usa proxys dinámicos para transacciones de la siguiente manera:

El proxy generado viene encima de Bean. Agrega comportamiento transnacional al frijol. Aquí el proxy genera dinámicamente en tiempo de ejecución utilizando JDK Reflection API.

Cuando se detiene una aplicación, el proxy se destruirá y solo tendremos interfaz y bean en el sistema de archivos.

En el ejemplo anterior tenemos interfaz. Pero en la mayoría de la implementación de la interfaz no es lo mejor. Así que Bean no implementa una interfaz, en ese caso usamos la herencia:

Para generar dichos proxies, Spring usa una biblioteca de terceros llamada CGLib .

CGLib ( C ode G eneration Lib rary) se basa en ASM , se utiliza principalmente para generar proxy que extiende bean y agrega comportamiento de bean en los métodos de proxy.

Ejemplos de JDK Dynamic Proxy y CGLib

Ref de primavera