Historia de la cuestión:
Los objetos proxy y el patrón Proxy aparecieron en Java para crear objetos sustitutos que pueden controlar el acceso a un objeto real, modificar su comportamiento o inyectar funciones transversales (seguridad, registro, métricas). Desde Java 1.3, existe soporte para proxy dinámicos estándar en el paquete java.lang.reflect, y más tarde, bibliotecas populares como CGLIB y ByteBuddy.
Problema:
No siempre es posible o conveniente modificar directamente la lógica de una clase existente. A menudo se requiere agregar de manera transparente comportamiento (como registro, almacenamiento en caché, transacciones) sin modificar el código fuente del objeto, lo cual es imposible usando la herencia estándar.
Solución:
Los objetos Proxy pueden implementarse de manera estática (a través de subclassing manual) y de manera dinámica (a través del mecanismo de reflexión o bytecode). Un proxy dinámico permite crear on-the-fly objetos que implementan la interfaz necesaria y delegan las llamadas al objeto real dentro del método invoke(), proporcionando flexibilidad para inyectar comportamiento externo.
Ejemplo de código:
import java.lang.reflect.*; interface Service { void doWork(); } class RealService implements Service { public void doWork() { System.out.println("Haciendo trabajo!"); } } class LoggingInvocationHandler implements InvocationHandler { private final Object target; public LoggingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Llamando: " + method.getName()); return method.invoke(target, args); } } Service real = new RealService(); Service proxy = (Service) Proxy.newProxyInstance( real.getClass().getClassLoader(), new Class[] {Service.class}, new LoggingInvocationHandler(real)); proxy.doWork(); // Se registra la llamada
Características clave:
¿Puede un Proxy dinámico estándar en Java trabajar con clases que no implementan interfaces?
No. El Proxy estándar en Java solo funciona con interfaces. Para clases sin interfaces (por ejemplo, si quieres proxy un clase normal) se necesitan herramientas externas (por ejemplo, CGLIB, ByteBuddy).
¿Se puede crear un proxy para una interfaz con métodos privados?
No. Las interfaces en Java no pueden contener métodos privados que necesiten ser proxy. El proxy dinámico implementa los métodos públicos de la interfaz. Desde Java 9 se introdujeron métodos privados por defecto, pero no son accesibles a través del proxy.
¿Cuál es la diferencia entre InvocationHandler y MethodInterceptor (CGLIB)?
InvocationHandler es la interfaz estándar de JDK utilizada para proxies dinámicos. Acepta llamadas a los métodos de la interfaz. MethodInterceptor es una interfaz de CGLIB que permite interceptar llamadas a métodos en cualquier clase a través de la herencia dinámica. La diferencia está en la aplicabilidad y el nivel: JDK solo trabaja con interfaces, CGLIB trabaja con cualquier clase.
Un ingeniero repite manualmente el código de registro en cada método de todos los servicios. Al agregar nuevos métodos, duplica constantemente la lógica, a menudo olvidando agregar las llamadas necesarias.
Ventajas:
Desventajas:
Se ha implementado AOP en el proyecto a través de un proxy dinámico: el registro y el control de transacciones se realizan a través de envolturas sustitutas, implementadas de forma centralizada.
Ventajas:
Desventajas: