„Hallo, Amigo.“

„Hallo, Rishi.“

„Heute erkläre ich Ihnen ein neues und sehr interessantes Thema: dynamische Proxys . “

„Java bietet mehrere Möglichkeiten, die Funktionalität einer bestimmten Klasse zu ändern…“

„Die erste Methode ist die Vererbung.“

„Der einfachste Weg, das Verhalten einer Klasse zu ändern, besteht darin, eine neue Klasse zu erstellen, die die ursprüngliche (Basis-)Klasse erbt und deren Methoden überschreibt. Anschließend verwenden Sie anstelle der ursprünglichen Klasse die abgeleitete Klasse. Beispiel:“

Reader reader = new UserCustomReader();

„Die zweite Methode besteht darin, eine Wrapper-Klasse zu verwenden.“

BufferedReader ist ein Beispiel für diesen Klassentyp. Erstens erbt es Reader . Mit anderen Worten, es kann anstelle von Reader verwendet werden. Zweitens leitet es alle Aufrufe an das ursprüngliche Reader- Objekt um, das an den Konstruktor des BufferedReader-Objekts übergeben werden muss . Zum Beispiel:"

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

„Die dritte Methode besteht darin, einen dynamischen Proxy (Proxy) zu erstellen.“

„Es gibt eine spezielle Klasse in Java (java.lang.reflect.Proxy), mit der Sie tatsächlich ein Objekt während der Programmausführung (dynamisch) erstellen können, ohne eine separate Klasse dafür zu erstellen.“

„Das geht ganz einfach:“

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

„Das ist schon etwas Neues für mich!“

„Aber natürlich brauchen wir kein Objekt ohne Methoden. Wir brauchen das Objekt, um Methoden zu haben, und wir brauchen sie, um das zu tun, was wir wollen. Java verwendet dafür eine spezielle Schnittstelle namens InvocationHandler , die alle Methodenaufrufe abfangen kann . “ mit dem Proxy-Objekt verknüpft . Ein Proxy-Objekt kann nur über Schnittstellen erstellt werden.

Invoke – ist der Standardname für eine Methode oder Klasse, deren Hauptaufgabe darin besteht, einfach eine Methode aufzurufen.“

Handler – ist der Standardname für eine Klasse, die ein Ereignis verarbeitet. Beispielsweise würde eine Klasse, die Mausklicks verarbeitet, MouseClickHandler usw. heißen.“

„Die InvocationHandler-Schnittstelle verfügt über eine einzige Aufrufmethode, an die alle Aufrufe des Proxy-Objekts weitergeleitet werden . Beispiel:“

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

„Beim Aufrufen der Methode „ reader . close () “ wird die Methode „invoke “ aufgerufen und auf dem Bildschirm wird „Ja!“ angezeigt.“

„Also haben wir eine CustomInvocationHandler- Klasse deklariert und die InvocationHandler- Schnittstelle und ihre Aufrufmethode implementiert . Wenn die Aufrufmethode aufgerufen wird, zeigt sie „Ja!“ an. Dann haben wir ein CustomInvocationHandler- Objekt erstellt und es beim Erstellen an die Methode newProxyInstance übergeben ein Proxy-Objekt.“

„Ja, das ist alles richtig.“

„Das ist ein sehr leistungsfähiges Tool. Normalerweise werden diese Proxys erstellt, um Objekte in Programmen zu simulieren, die physisch auf einem anderen Computer ausgeführt werden.  Oder um den Zugriff zu kontrollieren.“

„Mit dieser Methode können Sie die Berechtigungen des aktuellen Benutzers überprüfen, Fehler behandeln, Fehler protokollieren und vieles mehr.“

„Hier ist ein Beispiel, bei dem die Aufrufmethode auch die Methoden des ursprünglichen Objekts aufruft:“

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

„Dieses Beispiel weist zwei Besonderheiten auf.“

„Zuerst wird das „ursprüngliche“ Reader- Objekt an den Konstruktor übergeben und ein Verweis darauf im CustomInvocationHandler gespeichert .“

„Zweitens rufen wir dieselbe Methode noch einmal in der Aufrufmethode auf, dieses Mal jedoch für das „ursprüngliche“ Objekt.“

„Ah. Mit anderen Worten, diese letzte Zeile ruft dieselbe Methode auf, jedoch für das ursprüngliche Objekt:“

return method.invoke(readerOriginal, args);

"Ja."

„Ich würde nicht sagen, dass es super offensichtlich ist, aber es ist trotzdem verständlich. Zumindest scheint es so.“

„Großartig. Dann noch etwas. In der newProxyInstance-Methode müssen Sie etwas mehr Verwaltungsinformationen übergeben, um ein Proxy-Objekt zu erstellen. Da wir jedoch keine monströsen Proxy-Objekte erstellen, können diese Informationen leicht von der ursprünglichen Klasse selbst abgerufen werden. "

„Hier ist ein weiteres Beispiel:“

Code
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 und Liste der Schnittstellen. Das ist etwas aus Reflection, nicht wahr?“

"Ja."

„Ich verstehe. Nun, ich denke, ich kann ein primitives, supereinfaches Proxy-Objekt erstellen, falls ich jemals eines brauche.“

„Dann geh und melde dich bei Diego.“