"Oi, amigo."

"Olá, Rishi."

"Hoje vou explicar um tópico novo e muito interessante para vocês: proxies dinâmicos" .

"Java tem várias maneiras de alterar a funcionalidade de uma classe específica..."

"O primeiro método é a herança."

"A maneira mais fácil de alterar o comportamento de uma classe é criar uma nova classe que herde a classe original (base) e substitua seus métodos. Então, em vez de usar a classe original, você usa a classe derivada. Por exemplo:"

Reader reader = new UserCustomReader();

"O segundo método é usar uma classe wrapper."

" BufferedReader é um exemplo desse tipo de classe. Primeiro, herda Reader . Em outras palavras, pode ser usado no lugar de Reader. Segundo, redireciona todas as chamadas para o objeto Reader original , que deve ser passado para o construtor do objeto BufferedReader . Por exemplo:"

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

"O terceiro método é criar um proxy dinâmico (Proxy)."

"Existe uma classe especial em Java (java.lang.reflect.Proxy) que realmente permite que você construa um objeto durante a execução do programa (dinamicamente), sem criar uma classe separada para ele."

"Isso é muito fácil de fazer:"

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

"Isso já é algo novo para mim!"

"Mas é claro que não precisamos de um objeto sem métodos. Precisamos que o objeto tenha métodos e precisamos que eles façam o que queremos. Java usa uma interface especial para isso chamada InvocationHandler , que pode interceptar todas as chamadas de método associado ao objeto proxy. Um objeto proxy só pode ser criado usando interfaces."

" Invoke – é o nome padrão para um método ou classe cuja tarefa principal é simplesmente chamar algum método."

" Handler – é o nome padrão para uma classe que manipula algum evento. Por exemplo, uma classe que manipula cliques do mouse seria chamada de MouseClickHandler, etc."

"A interface InvocationHandler tem um único método de chamada, para o qual todas as chamadas para o objeto proxy são direcionadas . Por exemplo:"

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;
 }
}

"Ao chamar o método reader . close (), o método invoke será chamado e a tela exibirá 'yes!'"

"Portanto, declaramos uma classe CustomInvocationHandler e implementamos a interface InvocationHandler e seu método de chamada . Quando o método de chamada é chamado, ele exibe 'sim!'. Em seguida, criamos um objeto CustomInvocationHandler e o passamos para o método newProxyInstance ao criar um objeto proxy."

"Sim, está tudo correto."

"Esta é uma ferramenta muito poderosa. Normalmente, esses proxies são criados para simular objetos em programas que estão sendo executados fisicamente em outro computador.  Ou para controlar o acesso."

"Você pode verificar as permissões do usuário atual, lidar com erros, registrar erros e muito mais neste método."

"Aqui está um exemplo em que o método de chamada também chama os métodos do 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 exemplo tem duas características especiais."

"Primeiro, o objeto Reader «original» é passado para o construtor e uma referência a ele é salva dentro do CustomInvocationHandler ."

"Em segundo lugar, chamamos esse mesmo método novamente no método de chamada, mas no objeto «original» desta vez."

"Ah. Em outras palavras, esta última linha chama o mesmo método, mas no objeto original:"

return method.invoke(readerOriginal, args);

"Sim."

"Eu não diria que é super óbvio, mas ainda é compreensível. Ou assim parece."

"Ótimo. Mais uma coisa. No método newProxyInstance, você precisa passar um pouco mais de informações de manutenção para criar um objeto proxy. Mas como não estamos criando objetos proxy monstruosos, essas informações são facilmente obtidas da própria classe original. "

"Aqui está outro exemplo:"

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 e lista de interfaces. Isso é algo do Reflection, não é?"

"Sim."

"Entendo. Bem, acho que posso criar um objeto proxy primitivo e super simples, se precisar de um."

"Então vá checar com Diego."