Dynamische Proxy-Klassen

Inhalt

Einführung
Dynamische Proxy-API
Serialisierung
Beispiele

Einführung

Eine dynamische Proxy-Klasse ist eine Klasse, die eine zur Laufzeit angegebene Liste von Schnittstellen implementiert, sodass ein Methodenaufruf über eine der Schnittstellen einer Instanz der Klasse codiert und über eine einheitliche Schnittstelle an ein anderes Objekt gesendet wird. Somit kann eine dynamische Proxy-Klasse verwendet werden, um ein typsicheres Proxy-Objekt für eine Liste von Schnittstellen zu erstellen, ohne dass die Proxy-Klasse vorab generiert werden muss, z. B. mit Tools zur Kompilierungszeit.Methodenaufrufe für eine Instanz einer dynamischen Proxy-Klasse werden an eine einzelne Methode im invocationhandler der Instanz gesendet und mit einem java.lang.reflect.Method -Objekt codiert, das die aufgerufene Methode identifiziert, und einem Array vom Typ Object, das die Argumente enthält.

Dynamische Proxy-Klassen sind nützlich für eine Anwendung oder Bibliothek, die einen typsicheren reflektierenden Versand von Aufrufen auf Objekten bereitstellen muss, die Schnittstellen-APIs darstellen. Beispielsweise kann eine Anwendung eine dynamische Proxy-Klasse verwenden, um ein Objekt zu erstellen, das mehrere beliebige Ereignis-Listener-Schnittstellen implementiert – Schnittstellen thatextend java.util.EventListener -, um eine Vielzahl von Ereignissen verschiedener Typen auf einheitliche Weise zu verarbeiten, z. B. alle diese Ereignisse in einer Datei zu protokollieren.

Dynamische Proxy-Klassen-API

Eine dynamische Proxy-Klasse (im Folgenden einfach als Proxyclass bezeichnet) ist eine Klasse, die eine Liste von Interfaces implementiert, die zur Laufzeit beim Erstellen der Klasse angegeben werden.

Eine Proxy-Schnittstelle ist eine solche Schnittstelle, die istimplementiert durch eine Proxy-Klasse.

Eine Proxy-Instanz ist eine Instanz einer proxyclass.

Erstellen einer Proxy-Klasse

Proxy-Klassen sowie Instanzen davon werden mit erstelltdie statischen Methoden der Klasse java.lang.spiegeln.Proxy.

Die Proxy.getProxyClass -Methode gibt dasjava.lang.Class -Objekt für eine Proxy-Klasse mit einem Classloader und einem Array von Schnittstellen zurück. Die Proxy-Klasse wird im angegebenen Klassenlader definiert und implementiert alle bereitgestellten Schnittstellen. Wenn eine Proxy-Klasse für die gleiche Permutation von Schnittstellen bereits im Klassenlader definiert wurde, wird die vorhandene Proxy-Klasse zurückgegeben; andernfalls wird eine Proxy-Klasse für diese Schnittstellen dynamisch generiert und im Klassenlader definiert.

Es gibt mehrere Einschränkungen für die Parameter, die an Proxy.getProxyClass:

  • Alle Class Objekte im Arrayinterfaces müssen interfaces, notclasses oder primitive Typen darstellen.
  • Keine zwei Elemente im Array interfacesdürfen sich auf identische Class Objekte beziehen.
  • Alle Schnittstellentypen müssen über den angegebenen Klassenlader namentlich sichtbar sein. Mit anderen Worten, für den Klassenlader cl und jede Schnittstelle i muss followingexpression wahr sein:
     Class.forName(i.getName(), false, cl) == i
  • Alle nicht öffentlichen Schnittstellen müssen sich im selben Paket befinden; Andernfalls kann die Proxy-Klasse nicht alle Schnittstellen implementieren, unabhängig davon, in welchem Paket sie definiert ist.
  • Für jeden Satz von Mitgliedsmethoden der angegebenen Schnittstellen, die dieselbe Signatur haben:
    • Wenn der Rückgabetyp einer der Methoden ein primitiver Typ ist orvoid , dann müssen alle Methoden denselben Rückgabetyp haben.
    • Andernfalls muss eine der Methoden einen Rückgabetyp haben, der allen Rückgabetypen der übrigen Methoden zugewiesen werden kann.
  • Die resultierende Proxy-Klasse darf keine von der virtuellen Maschine auferlegten Grenzwerte überschreitenklassen. Beispielsweise kann die VM die Anzahl der Schnittstellen, die eine Klasse implementieren kann, auf 65535 begrenzen; In diesem Fall darf die Größe des Arrays interfaces 65535 nicht überschreiten.

Wenn eine dieser Einschränkungen verletzt wird, wirftProxy.getProxyClass eineIllegalArgumentException . Wenn das Array-Argumentinterfaces oder eines seiner Elementenull ist, wird ein NullPointerException gelöscht.

Beachten Sie, dass die Reihenfolge der angegebenen Proxy-Schnittstellen signifikant ist: Zwei Anforderungen für eine Proxy-Klasse mit derselben Schnittstellenkombination, jedoch in einer anderen Reihenfolge, führen zu zwei verschiedenen Proxy-Klassen. Proxy-Klassen werden durch die Reihenfolge ihrer Proxy-Schnittstellen unterschieden, um eine deterministische Methodenaufrufcodierung in Fällen bereitzustellen, in denen zwei oder mehr der Proxyinterfaces eine Methode mit demselben Namen und derselben Parametersignatur gemeinsam nutzen.

Damit nicht jedes Mal eine neue Proxy-Klasse generiert werden muss, wenn Proxy.getProxyClass mit demselben Klassenlader und derselben Schnittstellenliste aufgerufen wird, sollte die Implementierung der dynamischen Proxy-Klassen-API einen Cache mit generierten Proxyklassen enthalten, die von den entsprechenden Loadern und der Schnittstellenliste eingegeben werden.Die Implementierung sollte darauf achten, nicht auf die Klassenlader, Schnittstellen und Proxy-Klassen zu verweisen, um zu verhindern, dass Klassenlader und alle ihre Klassen bei Bedarf garbagecollected werden.

Proxy-Klasseneigenschaften

Eine Proxy-Klasse hat die folgenden Eigenschaften:

  • Proxy-Klassen sind public, final und nicht abstract.
  • Der unqualifizierte Name einer Proxy-Klasse ist nicht spezifiziert. Das Leerzeichen von Klassennamen, die mit der Zeichenfolge "$Proxy" beginnen, ist jedoch für Proxy-Klassen zu reservieren.
  • Eine Proxy-Klasse erweitert java.lang.reflect.Proxy.
  • Eine Proxy-Klasse implementiert genau die bei der Erstellung angegebenen Schnittstellen in derselben Reihenfolge.
  • Wenn eine Proxy-Klasse eine nicht öffentliche Schnittstelle implementiert, wird sie im selben Paket wie diese Schnittstelle definiert. Andernfalls ist das Paket einer Proxy-Klasse ebenfalls nicht angegeben. Beachten Sie, dass packagesealing nicht verhindert, dass eine Proxy-Klasse zur Laufzeit erfolgreich in einem bestimmten Paket definiert wird, und auch keine Klassen, die bereits im selben Klassenlader und im selben Paket mit bestimmten Unterzeichnern definiert sind.
  • Da eine Proxy-Klasse alle bei ihrer Erstellung angegebenen Schnittstellen implementiert, gibt der Aufruf von getInterfaces in seinemClass -Objekt ein Array zurück, das die gleiche Liste von Schnittstellen enthält (in der bei seiner Erstellung angegebenen Reihenfolge), der Aufruf von getMethods in seinem Class -Objektgibt ein Array von Method -Objekten zurück, die alle Methoden in diesen Schnittstellen enthalten, und der Aufruf vongetMethod findet Methoden in den Proxy-Schnittstellen, wie.
  • Die Proxy.isProxyClass -Methode gibt true zurück, wenn eine Proxy-Klasse übergeben wird – eine vonProxy.getProxyClass zurückgegebene Klasse oder die Klasse eines von Proxy.newProxyInstance zurückgegebenen Objekts – und andernfalls false. Die Zuverlässigkeit dieser Methode ist wichtig für die Fähigkeit, sie zu Verwendensicherheitsentscheidungen zu treffen, so sollte seine Implementierung nicht nur testen, ob die betreffende Klassejava.lang.reflect.Proxy erweitert.
  • Der java.security.ProtectionDomain einer Proxyclass ist derselbe wie der von Systemklassen, die vom Bootstrapclass-Lader geladen werden, z. B. java.lang.Object, da der Code für eine Proxy-Klasse von vertrauenswürdigem Systemcode generiert wird. Diese Schutzdomäne wird normalerweise java.security.AllPermission gewährt.

Erstellen einer Proxy-Instanz

Jede Proxy-Klasse verfügt über einen öffentlichen Konstruktor, der oneargument , eine Implementierung der Schnittstelle InvocationHandler .

Jeder Proxy-Instanz ist ein Aufrufhandlerobjekt zugeordnet, das an seinen Konstruktor übergeben wurde. Anstatt die Reflection-API für den Zugriff auf den öffentlichen Konstruktor verwenden zu müssen, kann eine Proxyinstance auch durch Aufrufen derProxy.newProxyInstance -Methode erstellt werden, die die Aktionen des Aufrufs von Proxy.getProxyClass mit dem Aufruf des Konstruktors mit einem Aufrufhandler kombiniert.Proxy.newProxyInstance wirftIllegalArgumentException aus den gleichen Gründen wieProxy.getProxyClass.

Proxy-Instanzeigenschaften

Eine Proxy-Instanz hat die folgenden Eigenschaften:

  • Bei einer Proxy-Instanz proxy und einer der von der Proxy-Klasse Foo implementierten Interfaces gibt der folgende Ausdruck true:
     proxy instanceof Foo

    und die folgende Cast-Operation wird erfolgreich sein (anstatt throwinga ClassCastException):

     (Foo) proxy
  • Die statische Proxy.getInvocationHandler -Methode gibt den Aufrufhandler zurück, der der als Argument übergebenen Proxy-Instanz zugeordnet ist. Wenn das anProxy.getInvocationHandler übergebene Objekt keine Proxy-Instanz ist, wird ein IllegalArgumentException ausgelöst.
  • Ein Schnittstellenmethodenaufruf auf einer Proxy-Instanz wird beencoded und an die invoke -Methode des Aufrufhandlers gesendet, wie unten beschrieben.

    Die Proxy-Instanz selbst wird als erstes Argument von invoke übergeben, das vom Typ Object ist.

    Das zweite Argument, das an invoke übergeben wird, ist diejava.lang.reflect.Method -Instanz, die der auf der Proxy-Instanz aufgerufenen interface-Methode entspricht. Die deklarierende classof des Method -Objekts ist die Schnittstelle, in der themethod deklariert wurde.

    Das dritte Argument, das an invoke übergeben wird, ist ein Array von Objekten, die die Werte der im Methodenaufruf für die Proxy-Instanz übergebenen Argumente enthalten. Argumente von primitivetypes werden in eine Instanz der entsprechenden primitivewrapper-Klasse eingeschlossen, z. B. java.lang.Integer oderjava.lang.Boolean. Die Implementierung der Methodeinvoke kann den Inhalt von thisarray ändern.

    Der von der Methode invoke zurückgegebene Wert wirdder Rückgabewert des Methodenaufrufs auf der Proxy-Instanz. Wenn der deklarierte Rückgabewert der Schnittstellenmethode ein primitivetype ist, muss der von invoke zurückgegebene Wert eine Instanz der entsprechenden primitiven Wrapper-Klasse sein. Wenn der von invoke zurückgegebene Wert null ist und der Rückgabetyp der interface-Methode primitiv ist, wird von der methodinvocation in der Proxy-Instanz ein NullPointerException ausgelöst. Wenn der voninvoke zurückgegebene Wert ansonsten nicht mit dem oben beschriebenen deklarierten Rückgabetyp der Methode kompatibel ist, wird von der Proxyinstance einClassCastException ausgelöst.

    Wenn eine Ausnahme von der invoke -Methode ausgelöst wird, wird sie auch vom Methodenaufruf in der Proxy-Instanz ausgelöst.Der Typ der Ausnahme muss entweder einem der in der Signatur der Schnittstellenmethode deklarierten Exception-Typen oder den nicht markierten Exception-Typen java.lang.RuntimeException oderjava.lang.Error zugewiesen werden können. Wenn voninvoke eine checked exception ausgelöst wird, die keinem der in der throws -Klausel der interfacemethod deklarierten exceptiontypes zugewiesen werden kann, wird vom Methodenaufruf für die Proxy-Instanz ein UndeclaredThrowableException ausgelöst. DieUndeclaredThrowableException wird mit der Ausnahme erstellt, die von der invoke -Methode ausgelöst wurde.

  • Ein Aufruf der hashCode -, equals – oder toString -Methoden, die injava.lang.Object auf einer Proxy-Instanz deklariert sind, wird codiert und an die invoke -Methode des Aufrufhandlers auf die gleiche Weise gesendet, wie Interface-Methodenaufrufe wie oben beschrieben codiert und gesendet werden. Die deklarierende Klasse des Method -Objekts, das an invoke übergeben wird, ist java.lang.Object. Andere öffentliche Methoden einer von java.lang.Object geerbten proxyinstance werden von einer Proxy-Klasse nicht überschrieben, sodass sich Aufrufe dieser Methoden wie bei Instanzen von java.lang.Object verhalten.

Methoden, die in MultipleProxy-Schnittstellen dupliziert werden

Wenn zwei oder mehr Schnittstellen einer Proxy-Klasse eine Methode mit demselben Namen und derselben Parametersignatur enthalten, wird die Reihenfolge der Schnittstellen der Proxyclass signifikant. Wenn eine solche duplicatemethod für eine Proxy-Instanz aufgerufen wird, ist das Method -Objekt, das an den Aufrufhandler übergeben wird, nicht unbedingt derjenige, dessen deklarierende Klasse aus dem Referenztyp der Schnittstelle zugewiesen werden kann, über die die Methode des Proxys aufgerufen wurde. Diese Einschränkung besteht, weil die entsprechende Methodenimplementierung in der generierten Proxy-Klasse nicht bestimmen kann, über welche Schnittstelle sie aufgerufen wurde. Wenn daher eine doppelte Methode in einer Proxy-Instanz aufgerufen wird, wird das Method -Objekt für die Methode in der ersten Schnittstelle, die die Methode enthält (entweder direkt oder über eine Superschnittstelle geerbt), in der Liste der Schnittstellen der Proxy-Klasse an die invoke -Methode des Aufrufhandlers übergeben, unabhängig vom Referenztyp, über den der Methodenaufruf erfolgt ist.

Wenn eine Proxy-Schnittstelle eine Methode mit demselben Namen und derselben Parametersignatur enthält wie die hashCode -,equals – oder toString -Methoden vonjava.lang.Object, wenn eine solche Methode in einer aufgerufen wirdproxy-Instanz, das Method -Objekt, das an den invocation-Handler übergeben wird, hat java.lang.Object als Deklarationsklasse. Mit anderen Worten, die öffentlichen, nicht endgültigen Methoden vonjava.lang.Object gehen logisch allen Proxyinterfaces voraus, um zu bestimmen, welches Method -Objekt an den Aufrufhandler übergeben werden soll.

Beachten Sie auch, dass die invoke -Methode beim Senden einer doppelten Methode an einen invocation-Handler nur geprüfte Ausnahmetypen auslösen darf, die einem der exceptiontypes in der throws -Klausel der Methode in allen Proxy-Schnittstellen zugewiesen werden können, über die sie aufgerufen werden kann. Wenn dieinvoke -Methode eine checked Ausnahme auslöst, die keinem der von der Methode deklarierten Ausnahmetypen in einer der Proxy-Schnittstellen zugewiesen werden kann, über die sie aufgerufen werden kann, wird anunchecked UndeclaredThrowableException durch den Aufruf der Proxy-Instanz ausgelöst. Diese Einschränkung bedeutet, dass nicht alle Ausnahmetypen, die durch Aufrufen von getExceptionTypes für das Method -Objekt zurückgegeben werden, das an die invoke -Methode übergeben wurde, notwendigerweise erfolgreich von der invoke -Methode ausgelöst werden können.

Serialisierung

Da java.lang.reflect.Proxyjava.io.Serializable implementiert, können Proxy-Instanzen wie in diesem Abschnitt beschrieben serialisiert werden. Wenn eine Proxy-Instanz einen Aufrufhandler enthält, der jedoch nicht java.io.Serializable zugewiesen werden kann, wird einejava.io.NotSerializableException ausgelöst, wenn eine solche Instanz in einejava.io.ObjectOutputStream geschrieben wird. Beachten Sie, dass die Implementierung von java.io.Externalizable für Proxyclassen in Bezug auf die Serialisierung den gleichen Effekt hat wie die Implementierung vonjava.io.Serializable: Die Methoden writeExternal und readExternal der SchnittstelleExternalizable werden niemals auf einer aufgerufen Proxyinstanz (oder ein Aufrufhandler) als Teil seines Serialisierungsprozesses. Wie bei allen Class -Objekten ist dasClass -Objekt für eine Proxy-Klasse immer serialisierbar.

Eine Proxy-Klasse hat keine serialisierbaren Felder und eineserialVersionUID von 0L. Mit anderen Worten, wenn das Class -Objekt für eine Proxy-Klasse an die statische lookup -Methode vonjava.io.ObjectStreamClass übergeben wird, hat die zurückgegebeneObjectStreamClass -Instanz die folgenden Eigenschaften:

  • Das Aufrufen der Methode getSerialVersionUID gibt 0L zurück.
  • Wenn Sie die getFields -Methode aufrufen, wird ein Array der Länge Null zurückgegeben.
  • Wenn Sie die getField -Methode mit einem beliebigenString -Argument aufrufen, wird null zurückgegeben.

Das Stream-Protokoll für die Objektserialisierung unterstützt einen Typcode mit dem Namen TC_PROXYCLASSDESC, der ein Terminalsymbol in der Grammatik für das Stream-Format ist; sein Typ und Wert werden durch das folgende konstante Feld in derjava.io.ObjectStreamConstants Schnittstelle definiert:

 final static byte TC_PROXYCLASSDESC = (byte)0x7D;

Die Grammatik enthält auch die folgenden zwei Regeln, wobei die erste eine alternative Erweiterung der ursprünglichen newClassDescrule:

newClassDesc:
TC_PROXYCLASSDESCnewHandle proxyClassDescInfo

proxyClassDescInfo:
(int)<count>proxyInterfaceName classAnnotationsuperClassDesc

proxyInterfaceName:
(utf)

Wenn ein ObjectOutputStream den classdescriptor für eine Klasse serialisiert, die eine Proxy-Klasse ist, wie unter Umgehung seines Class -Objekts an dieProxy.isProxyClass -Methode bestimmt, verwendet er den TC_PROXYCLASSDESC -Typcode anstelle vonTC_CLASSDESC gemäß den obigen Regeln. In der Erweiterung von proxyClassDescInfo sind die sequence ofproxyInterfaceName-Elemente die Namen aller von der Proxy-Klasse implementierten Interfaces in der Reihenfolge, in der sie durch Aufrufen der getInterfaces -Methode für das Class -Objekt zurückgegeben werden. Die Elemente classAnnotation undsuperClassDesc haben dieselbe Bedeutung wie in der Regel Classdescinfo. Für eine Proxy-Klasse ist superClassDescis der Klassendeskriptor für seine Superklassejava.lang.reflect.Proxy; Einschließlich dieses Deskriptorsermöglicht die Entwicklung der serialisierten Darstellung derklasse Proxy für Proxy-Instanzen.

Für Nicht-Proxy-Klassen ruft ObjectOutputStream die Methode itsprotected annotateClass auf, damit Unterklassen benutzerdefinierte Daten für eine bestimmte Klasse in den Stream schreiben können. Für proxyclasses wird anstelle von annotateClass die folgende Methode in java.io.ObjectOutputStream mit dem Class -Objekt für die Proxy-Klasse aufgerufen:

 protected void annotateProxyClass(Class cl) throws IOException;

Die Standardimplementierung von annotateProxyClass inObjectOutputStream bewirkt nichts.

Wenn ein ObjectInputStream auf den TypcodeTC_PROXYCLASSDESC , deserialisiert er den Classdescriptor für eine Proxy-Klasse aus dem Stream, der wie oben beschrieben formatiert ist. Anstatt die resolveClass -Methode aufzurufen, um das Class -Objekt für den classdescriptor aufzulösen, wird die folgende Methode injava.io.ObjectInputStream aufgerufen:

 protected Class resolveProxyClass(String interfaces) throws IOException, ClassNotFoundException;

Die Liste der Schnittstellennamen, die im Proxyclass-Deskriptor deserialisiert wurden, wird als interfaces -Argument an resolveProxyClass übergeben.

Die Standardimplementierung von resolveProxyClass inObjectInputStream gibt die Ergebnisse des Aufrufs vonProxy.getProxyClass mit der Liste derClass -Objekte für die im Parameterinterfaces genannten Schnittstellen zurück. Das Class -Objekt, das für jeden Schnittstellennamen i verwendet wird, ist der Wert, der durch Aufrufen von

 Class.forName(i, false, loader)

neu abgestimmt wurde, wobeiloaderder erste Nicht-Null-Klassenlader auf dem Ausführungsstapel ist, odernull, wenn sich keine Nicht-Null-Klassenlader auf dem Stapel befinden. Dies ist dieselbe Auswahl des Klassenladeprogramms, die durch das Standardverhalten der MethoderesolveClassgetroffen wurde. Dieser samevalue vonloaderist auch der Klassenlader, der anProxy.getProxyClass. WennProxy.getProxyClasseinIllegalArgumentExceptionwirft, wirftresolveClasseinClassNotFoundExceptionmit demIllegalArgumentException.

Da eine Proxy-Klasse niemals eigene serialisierbare Felder hat, besteht theclassdata in der Stream-Darstellung einer Proxy-Instanzbesteht vollständig aus den Instanzdaten für seine Oberklassejava.lang.reflect.Proxy. Proxy hat ein serialisierbares Feld, h , das den Aufrufhandler für die Proxy-Instanz enthält.

Beispiele

Hier ist ein einfaches Beispiel, das eine Nachricht vor und nach einem Methodenaufruf für ein Objekt ausgibt, das eine beliebige Liste von Schnittstellen implementiert:

public interface Foo { Object bar(Object obj) throws BazException;}public class FooImpl implements Foo { Object bar(Object obj) throws BazException { // ... }}public class DebugProxy implements java.lang.reflect.InvocationHandler { private Object obj; public static Object newInstance(Object obj) { return java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new DebugProxy(obj)); } private DebugProxy(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method m, Object args) throws Throwable { Object result; try { System.out.println("before method " + m.getName()); result = m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("after method " + m.getName()); } return result; }}

Um eine DebugProxy für eine Implementierung der Foo Schnittstelle zu konstruieren und eine ihrer Methoden aufzurufen:

 Foo foo = (Foo) DebugProxy.newInstance(new FooImpl()); foo.bar(null);

Hier ist ein Beispiel für eine Utility-Aufrufhandlerklasse, die das Standard-Proxy-Verhalten für Methoden bereitstellt, die vonjava.lang.Object geerbt wurden, und die Delegierung bestimmter Proxymethodenaufrufe an unterschiedliche Objekte implementiert, abhängig von der Schnittstelle der aufgerufenen Methode:

import java.lang.reflect.*;public class Delegator implements InvocationHandler { // preloaded Method objects for the methods in java.lang.Object private static Method hashCodeMethod; private static Method equalsMethod; private static Method toStringMethod; static { try { hashCodeMethod = Object.class.getMethod("hashCode", null); equalsMethod = Object.class.getMethod("equals", new Class { Object.class }); toStringMethod = Object.class.getMethod("toString", null); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } private Class interfaces; private Object delegates; public Delegator(Class interfaces, Object delegates) { this.interfaces = (Class) interfaces.clone(); this.delegates = (Object) delegates.clone(); } public Object invoke(Object proxy, Method m, Object args) throws Throwable { Class declaringClass = m.getDeclaringClass(); if (declaringClass == Object.class) { if (m.equals(hashCodeMethod)) { return proxyHashCode(proxy); } else if (m.equals(equalsMethod)) { return proxyEquals(proxy, args); } else if (m.equals(toStringMethod)) { return proxyToString(proxy); } else { throw new InternalError( "unexpected Object method dispatched: " + m); } } else { for (int i = 0; i < interfaces.length; i++) { if (declaringClass.isAssignableFrom(interfaces)) { try { return m.invoke(delegates, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } } return invokeNotDelegated(proxy, m, args); } } protected Object invokeNotDelegated(Object proxy, Method m, Object args) throws Throwable { throw new InternalError("unexpected method dispatched: " + m); } protected Integer proxyHashCode(Object proxy) { return new Integer(System.identityHashCode(proxy)); } protected Boolean proxyEquals(Object proxy, Object other) { return (proxy == other ? Boolean.TRUE : Boolean.FALSE); } protected String proxyToString(Object proxy) { return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); }}

Unterklassen von Delegator könneninvokeNotDelegated überschreiben, um das Verhalten von Proxymethodenaufrufen zu implementieren, die nicht direkt an andere Objekte delegiert werden sollen, und sie können proxyHashCode,proxyEquals und proxyToString überschreiben, um das Standardverhalten der Methoden zu überschreiben, die der Proxy von java.lang.Object erbt.

Um eine Delegator für eine Implementierung der Foo Schnittstelle zu konstruieren:

 Class proxyInterfaces = new Class { Foo.class }; Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), proxyInterfaces, new Delegator(proxyInterfaces, new Object { new FooImpl() }));

Beachten Sie, dass die Implementierung der oben angegebenen Delegator -Klasse eher illustrativ als optimiert sein soll; anstatt beispielsweise die Method -Objekte für die Methoden hashCode, equals undtoString zwischenzuspeichern und zu vergleichen, können sie nur anhand ihrer Methodennamen abgeglichen werden, da keiner dieser Methodennamen injava.lang.Object überladen ist.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.