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
interfaces
dürfen sich auf identischeClass
Objekte beziehen. - Alle Schnittstellentypen müssen über den angegebenen Klassenlader namentlich sichtbar sein. Mit anderen Worten, für den Klassenlader
cl
und jede Schnittstellei
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 vongetMethods
in seinemClass
-Objektgibt ein Array vonMethod
-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 vonProxy.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 normalerweisejava.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-KlasseFoo
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 einIllegalArgumentException
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 TypObject
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 desMethod
-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 voninvoke
zurückgegebene Wert eine Instanz der entsprechenden primitiven Wrapper-Klasse sein. Wenn der voninvoke
zurückgegebene Wertnull
ist und der Rückgabetyp der interface-Methode primitiv ist, wird von der methodinvocation in der Proxy-Instanz einNullPointerException
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-Typenjava.lang.RuntimeException
oderjava.lang.Error
zugewiesen werden können. Wenn voninvoke
eine checked exception ausgelöst wird, die keinem der in derthrows
-Klausel der interfacemethod deklarierten exceptiontypes zugewiesen werden kann, wird vom Methodenaufruf für die Proxy-Instanz einUndeclaredThrowableException
ausgelöst. DieUndeclaredThrowableException
wird mit der Ausnahme erstellt, die von derinvoke
-Methode ausgelöst wurde. - Ein Aufruf der
hashCode
-,equals
– odertoString
-Methoden, die injava.lang.Object
auf einer Proxy-Instanz deklariert sind, wird codiert und an dieinvoke
-Methode des Aufrufhandlers auf die gleiche Weise gesendet, wie Interface-Methodenaufrufe wie oben beschrieben codiert und gesendet werden. Die deklarierende Klasse desMethod
-Objekts, das aninvoke
übergeben wird, istjava.lang.Object
. Andere öffentliche Methoden einer vonjava.lang.Object
geerbten proxyinstance werden von einer Proxy-Klasse nicht überschrieben, sodass sich Aufrufe dieser Methoden wie bei Instanzen vonjava.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.Proxy
java.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
gibt0L
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, wirdnull
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_PROXYCLASSDESC
newHandle 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, wobeiloader
der 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 MethoderesolveClass
getroffen wurde. Dieser samevalue vonloader
ist auch der Klassenlader, der anProxy.getProxyClass
. WennProxy.getProxyClass
einIllegalArgumentException
wirft, wirftresolveClass
einClassNotFoundException
mit 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.