"Hola, amigo".

"Hola, Rishi".

"Hoy les explicaré un tema nuevo y muy interesante: los proxies dinámicos" .

"Java tiene varias formas de cambiar la funcionalidad de una clase en particular..."

"El primer método es la herencia".

"La forma más fácil de cambiar el comportamiento de una clase es crear una nueva clase que herede la clase original (base) y anular sus métodos. Luego, en lugar de usar la clase original, usa la clase derivada. Por ejemplo:"

Reader reader = new UserCustomReader();

"El segundo método es usar una clase contenedora".

" BufferedReader es un ejemplo de este tipo de clase. Primero, hereda Reader . En otras palabras, se puede usar en lugar de Reader. Segundo, redirige todas las llamadas al objeto Reader original , que debe pasarse al constructor del objeto BufferedReader. . Por ejemplo:"

Reader readerOriginal = new UserCustomReader();
Reader reader = new BufferedReader(readerOriginal);

"El tercer método es crear un proxy dinámico (Proxy)".

"Hay una clase especial en Java (java.lang.reflect.Proxy) que realmente te permite construir un objeto durante la ejecución del programa (dinámicamente), sin crear una clase separada para él".

"Esto es muy fácil de hacer:"

Reader reader = (Reader)Proxy.newProxyInstance();

"¡Eso ya es algo nuevo para mí!"

"Pero, por supuesto, no necesitamos un objeto sin métodos. Necesitamos que el objeto tenga métodos, y los necesitamos para hacer lo que queremos. Java usa una interfaz especial para esto llamada InvocationHandler, que puede interceptar todas las llamadas a métodos asociado con el objeto proxy. Un objeto proxy solo se puede crear utilizando interfaces".

" Invocar : es el nombre estándar de un método o clase cuya tarea principal es simplemente llamar a algún método".

" Manejador : es el nombre estándar para una clase que maneja algún evento. Por ejemplo, una clase que maneja los clics del mouse se llamaría MouseClickHandler, etc."

"La interfaz InvocationHandler tiene un único método de invocación, al que se dirigen todas las llamadas al objeto proxy . Por ejemplo:"

Código
Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler());
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  System.out.println("yes!");
  return null;
 }
}

"Al llamar al método reader . close (), se llamará al método de invocación y la pantalla mostrará '¡sí!'"

"Entonces, declaramos una clase CustomInvocationHandler , e implementamos la interfaz InvocationHandler y su método de invocación . Cuando se llama al método de invocación, muestra '¡sí!'. Luego creamos un objeto CustomInvocationHandler y lo pasamos al método newProxyInstance al crear un objeto proxy".

"Sí, eso es todo correcto".

"Esta es una herramienta muy poderosa. Por lo general, estos proxies se crean para simular objetos en programas que se ejecutan físicamente en otra computadora.  O para controlar el acceso".

"Puede verificar los permisos del usuario actual, manejar errores, registrar errores y mucho más en este método".

"Aquí hay un ejemplo en el que el método de invocación también llama a los métodos del objeto original:"

Código
Reader original = new UserCustomReader();

Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler(original));
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 private Reader readerOriginal;

 CustomInvocationHandler(Reader readerOriginal)
 {
  this.readerOriginal = readerOriginal;
 }

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  if (method.getName().equals("close"))
  {
   System.out.println("Reader closed!");
  }

  // This calls the readerOriginal object's close method.
  // The method's name and a description of its parameters are stored in the method variable.
  return method.invoke(readerOriginal, args);
 }
}

"Este ejemplo tiene dos características especiales".

"Primero, el objeto Reader «original» se pasa al constructor y se guarda una referencia dentro de CustomInvocationHandler ".

"Segundo, volvemos a llamar a este mismo método en el método de invocación, pero esta vez en el objeto «original»".

"Ah. En otras palabras, esta última línea llama al mismo método, pero en el objeto original:"

return method.invoke(readerOriginal, args);

"Sí."

"No diría que es muy obvio, pero aun así es comprensible. O eso parece".

"Genial. Luego, una cosa más. En el método newProxyInstance, debe pasar un poco más de información de limpieza para crear un objeto proxy. Pero como no estamos creando objetos proxy monstruosos, esta información se obtiene fácilmente de la clase original en sí. "

"Aquí hay otro ejemplo:"

Código
Reader original = new UserCustomReader();

ClassLoader classLoader = original.getClass().getClassLoader();
Class<?>[] interfaces = original.getClass().getInterfaces();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(original);

Reader reader = (Reader)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  return null;
 }
}

"Ah. ClassLoader y lista de interfaces. Esto es algo de Reflection, ¿no?"

"Sí."

"Ya veo. Bueno, creo que puedo crear un objeto proxy súper simple y primitivo si alguna vez necesito uno".

"Entonces ve a hablar con Diego".