"Ciao, Amico."

"Ciao Rishi."

"Oggi vi spiego un argomento nuovo e molto interessante: i proxy dinamici" .

"Java ha diversi modi per modificare la funzionalità di una particolare classe..."

"Il primo metodo è l'ereditarietà."

"Il modo più semplice per modificare il comportamento di una classe è creare una nuova classe che erediti la classe originale (di base) e sovrascriverne i metodi. Quindi, invece di utilizzare la classe originale, si utilizza la classe derivata. Ad esempio:"

Reader reader = new UserCustomReader();

"Il secondo metodo consiste nell'utilizzare una classe wrapper."

" BufferedReader è un esempio di questo tipo di classe. In primo luogo, eredita Reader . In altre parole, può essere utilizzato al posto di Reader. In secondo luogo, reindirizza tutte le chiamate all'oggetto Reader originale , che deve essere passato al costruttore dell'oggetto BufferedReader . Per esempio:"

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

"Il terzo metodo consiste nel creare un proxy dinamico (Proxy)."

"Esiste una classe speciale in Java (java.lang.reflect.Proxy) che in realtà ti consente di costruire un oggetto durante l'esecuzione del programma (dinamicamente), senza creare una classe separata per esso."

"Questo è molto facile da fare:"

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

"Questo è già qualcosa di nuovo per me!"

"Ma ovviamente, non abbiamo bisogno di un oggetto senza metodi. Abbiamo bisogno che l'oggetto abbia metodi e abbiamo bisogno che facciano ciò che vogliamo. Java utilizza un'interfaccia speciale per questo chiamata InvocationHandler , che può intercettare tutte le chiamate ai metodi associato all'oggetto proxy. Un oggetto proxy può essere creato solo utilizzando le interfacce."

" Invoke – è il nome standard per un metodo o una classe il cui compito principale è semplicemente chiamare un metodo."

" Handler – è il nome standard per una classe che gestisce un evento. Ad esempio, una classe che gestisce i clic del mouse si chiamerebbe MouseClickHandler, ecc."

"L'interfaccia InvocationHandler ha un unico metodo invoke, a cui vengono indirizzate tutte le chiamate all'oggetto proxy . Ad esempio:"

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

"Quando si chiama il metodo reader . close (), verrà chiamato il metodo invoke e lo schermo visualizzerà 'yes!'"

"Quindi, abbiamo dichiarato una classe CustomInvocationHandler e implementato l' interfaccia InvocationHandler e il suo metodo invoke . Quando il metodo invoke viene chiamato, visualizza 'yes!'. Quindi abbiamo creato un oggetto CustomInvocationHandler e lo abbiamo passato al metodo newProxyInstance durante la creazione un oggetto proxy."

"Sì, è tutto corretto."

"Questo è uno strumento molto potente. Di solito, questi proxy vengono creati per simulare oggetti in programmi che sono fisicamente in esecuzione su un altro computer.  O per controllare l'accesso."

"Puoi controllare le autorizzazioni dell'utente corrente, gestire gli errori, registrare gli errori e molto altro con questo metodo."

"Ecco un esempio in cui il metodo invoke chiama anche i metodi dell'oggetto originale:"

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

"Questo esempio ha due caratteristiche speciali."

"In primo luogo, l' oggetto Reader «originale» viene passato al costruttore e un riferimento ad esso viene salvato all'interno di CustomInvocationHandler ."

"In secondo luogo, chiamiamo di nuovo lo stesso metodo nel metodo invoke, ma questa volta sull'oggetto «originale»."

"Ah. In altre parole, quest'ultima riga chiama lo stesso metodo, ma sull'oggetto originale:"

return method.invoke(readerOriginal, args);

"Sì."

"Non direi che è super ovvio, ma è comunque comprensibile. O almeno così sembra."

"Fantastico. Poi un'altra cosa. Nel metodo newProxyInstance, devi passare un po' più di informazioni di pulizia per creare un oggetto proxy. Ma poiché non stiamo creando oggetti proxy mostruosi, queste informazioni possono essere ottenute facilmente dalla classe originale stessa. "

"Ecco un altro esempio:"

Codice
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 ed elenco di interfacce. Questo è qualcosa di Reflection, vero?"

"Sì."

"Capisco. Beh, penso di poter creare un oggetto proxy primitivo e super semplice se mai ne avessi bisogno."

"Allora vai a controllare con Diego."