1. Definition des Proxy -Modus
Geben Sie einem Objekt ein Proxy -Objekt an, und das Proxy -Objekt steuert den Zugriff auf das ursprüngliche Objekt, dh der Client manipuliert das ursprüngliche Objekt nicht direkt, sondern manipuliert das ursprüngliche Objekt indirekt über das Proxy -Objekt.
Ein Beispiel für ein berühmtes Proxy -Muster ist die Referenzzählung: Wenn mehrere Kopien eines komplexen Objekts benötigt werden, kann das Proxy -Muster mit dem Meta -Modus kombiniert werden, um die Menge des Speichers zu reduzieren. Ein typischer Ansatz besteht darin, ein komplexes Objekt und ein mehrfacher Proxy zu erstellen, wobei jeder Proxy auf das ursprüngliche Objekt bezieht. Die Operationen, die auf den Agenten wirken, werden an das ursprüngliche Objekt weitergeleitet. Sobald nicht alle Agenten existieren, werden komplexe Objekte entfernt.
Es ist einfach, das Proxy -Modell zu verstehen, aber tatsächlich gibt es ein Proxy -Modell im Leben:
Wir können Zugkarten am Bahnhof kaufen, aber wir können sie auch im Zugangebot des Zugkartens kaufen. Das Training Ticket -Verkaufsbüro hier ist der Agent für Ticketkäufe am Bahnhof. Das heißt, wir stellen eine Ticketkaufanfrage im Verkaufsangebot aus. Die Verkaufsstelle sendet die Anfrage an den Bahnhof, und der Bahnhof sendet die erfolgreiche Antwort auf den Kauf an die Verkaufsstelle, und die Verkaufsstelle wird es Ihnen erneut mitteilen.
Tickets können jedoch nur an der Verkaufsstelle gekauft werden, jedoch nicht im Verkaufsrückerstattung, während Tickets am Bahnhof gekauft werden können, sodass sich die vom Agent unterstützten Betriebsvorgänge von denen des beauftragten Objekts unterscheiden können.
Lassen Sie mich Ihnen ein weiteres Beispiel geben, dem Sie beim Schreiben eines Programms begegnen werden:
Wenn ein vorhandenes Projekt vorhanden ist (Sie haben keinen Quellcode, können Sie es nur aufrufen), das Int Compute (String Exp1) aufrufen kann, um die Berechnung des Suffix -Ausdrucks zu implementieren. Wenn Sie dieses Projekt verwenden möchten, um die Berechnung des Infix -Ausdrucks zu implementieren, können Sie eine Proxy -Klasse schreiben und einen Computer definieren (String Exp2). Dieser Exp2 -Parameter ist ein Infix -Expression. Daher müssen Sie den Infix -Expression in einen Suffix -Expression (Preprozess) umwandeln, bevor Sie den COMPUTE () des vorhandenen Projekts aufrufen und dann den Compute () des vorhandenen Projekts aufrufen. Natürlich können Sie auch den Rückgabewert erhalten und andere Vorgänge durchführen, z. B. das Speichern der Datei (Postprozess). Dieser Prozess verwendet den Proxy -Modus.
Bei Verwendung eines Computers begegnen Sie auch Proxy -Modus -Anwendungen:
Remote -Proxy: Wir können aufgrund der GFW in China nicht auf Facebook zugreifen. Wir können darauf zugreifen, indem wir die Wand durchsuchen (ein Proxy einrichten). Der Zugangsprozess ist:
(1) Der Benutzer sendet die HTTP -Anfrage an den Proxy
(2) Der Proxy sendet eine HTTP -Anforderung an den Webserver
(3) Der Webserver sendet die HTTP -Antwort an den Proxy
(4) Der Proxy sendet die HTTP -Antwort an den Benutzer zurück
2. statischer Proxy
Der sogenannte statische Proxy bedeutet, dass während der Zusammenstellung eine Proxy-Klasse generiert wird, um eine Reihe von Operationen auf dem Proxy-Objekt abzuschließen. Das Folgende ist das Strukturklassendiagramm des Proxy -Musters:
1. Teilnehmer des Proxy -Modells
Im Proxy -Modus gibt es vier Rollen:
Themenschnittstelle: Das heißt die von der Proxy -Klasse implementierte Verhaltensschnittstelle.
Zielobjekt: Das heißt, das Objekt wird vervollständigt.
Proxy -Objekt: Der Proxy -Client, mit dem die reale Themenklasse eingekapselt wurde, ist die folgende Klassendiagrammstruktur des Proxy -Musters:
2. Ideen zur Implementierung des Agentenmodells
Sowohl das Proxy -Objekt als auch das Zielobjekt implementieren dieselbe Verhaltensschnittstelle.
Die Proxy -Klasse und die Zielklasse implementieren die Schnittstellenlogik separat.
Instantieren Sie ein Zielobjekt im Konstruktor der Proxy -Klasse.
Aufrufen der Verhaltensschnittstelle des Zielobjekts in der Proxy -Klasse.
Wenn der Client die Verhaltensschnittstelle des Zielobjekts aufrufen möchte, kann er nur über die Proxy -Klasse arbeiten.
3. Beispiele für statische Proxy
Das Folgende ist ein faules Ladebeispiel, um den statischen Proxy zu veranschaulichen. Wenn wir ein Service -System starten, kann es lange dauern, eine bestimmte Klasse zu laden. Um eine bessere Leistung zu erzielen, initialisieren wir diese komplexe Klasse beim Starten des Systems häufig nicht, sondern initialisieren ihre Proxy -Klasse. Dadurch werden die ressourcenkonsumierenden Methoden unter Verwendung von Proxy zur Trennung getrennt, wodurch die Startgeschwindigkeit des Systems beschleunigt und die Wartezeit des Benutzers verkürzt werden kann.
Definieren Sie eine Themenschnittstelle
public interface fach {public void Sayshello (); public void sagtgoodbye ();} Definieren Sie eine Zielklasse und implementieren Sie die Themenschnittstelle
public class RealSubject implementiert Fach {public void Sayshello () {System.out.println ("Hallo Welt"); } public void saygoodBye () {System.out.println ("Goodbye World"); }} Definieren Sie eine Proxy -Klasse, um das Zielobjekt zu proxy.
öffentliche Klasse staticProxy implementiert das Thema {private realSubject realSubject = null; public staticProxy () {} public void SayShello () {// Es wird zu diesem Zeitpunkt geladen, faul laden if (realSubject == null) {realSubject = new RealSubject (); } realSubject.sayhello (); } // Saygoodbye -Methode ist die gleiche ...} Einen Kunden definieren
Public Class Client {public static void main (String [] args) {staticProxy sp = new staticProxy (); sp.sayhello (); sp.saygoodbye (); }}Das obige ist ein einfaches Testbeispiel für einen statischen Proxy. Es kann sich nicht praktisch anfühlen. Dies ist jedoch nicht der Fall. Mit einem Proxy können wir auch die Zielobjektmethoden transformieren. Beispielsweise wird eine Reihe von Verbindungen im Datenbankverbindungspool erstellt. Um sicherzustellen, dass Verbindungen selten geöffnet werden, sind diese Verbindungen fast nie geschlossen. Wir haben jedoch immer die Angewohnheit, die offene Verbindung zu schließen. Auf diese Weise können wir den Proxy -Modus verwenden, um die Schließmethode in der Verbindungsschnittstelle neu zu steigern und ihn in den Datenbankverbindungspool zu recyceln, anstatt die Methode der Verbindungsnummer zu erreichen. Es gibt viele andere Beispiele, und Sie müssen sie selbst erleben.
3.. Dynamischer Agent
Dynamic Proxy bezieht sich auf dynamische Generierung von Proxy -Klassen zur Laufzeit. Das heißt, der Bytecode der Proxy -Klasse wird zur Laufzeit zum Klassenloader des aktuellen Proxy generiert und geladen. Im Vergleich zu statischen Verarbeitungsklassen haben dynamische Klassen viele Vorteile.
Es ist nicht erforderlich, eine vollständig identische Kapselungsklasse für das eigentliche Thema zu schreiben. Wenn es in der Themenoberfläche viele Methoden gibt, ist es auch problematisch, für jede Schnittstelle eine Proxy -Methode zu schreiben. Wenn sich die Schnittstelle ändert, müssen die tatsächlichen Themen- und Proxy -Klassen geändert werden, was der Systemwartung nicht förderlich ist.
Mithilfe einiger dynamischer Methoden zur Erzeugung von Proxy können sogar die Ausführungslogik der Proxy -Klasse zur Laufzeit formulieren und damit die Flexibilität des Systems erheblich verbessern.
Es gibt viele Möglichkeiten, dynamische Proxy zu generieren: Der JDK verfügt über dynamische Proxy, CGlib, Javassist usw. Diese Methoden haben ihre eigenen Vor- und Nachteile. In diesem Artikel wird hauptsächlich die Verwendung von dynamischen Proxy- und Quellcodeanalysen in JDK untersucht.
Hier ist ein Beispiel, um die Verwendung von dynamischem Proxy in JDK zu erklären:
öffentliche Klasse DynamicProxy implementiert InvocationHandler {private realSubject = null; öffentliches Objekt invoke (Object Proxy, Methode Methode, Object [] args) {if (realSubject == null) {realSubject = new RealSubject (); } method.invoke (realSubject, args); realSubject zurückgeben; }}Client -Code -Beispiel
Public Class Client {public static void main (Strings [] args) {Subjekt thema = (Subjekt) proxy.newinstance (classloader.getSystemLoader (), RealSubject.class.getInterfaces (), New DynamicProxy ()); Betreff.Sayhello (); Betreff.SayGoodBye (); }}Wie aus dem obigen Code hervorgeht, müssen wir dynamische Proxy in JDK verwenden. Verwenden Sie die statische Methode Proxy.Newinstance (Classloader, Schnittstellen [], InvokeHandler), um eine dynamische Proxy -Klasse zu erstellen. Die NewInStance -Methode enthält drei Parameter, die den Klassenlader, eine Liste von Schnittstellen darstellen, die die Proxy -Klasse implementieren soll, und eine Instanz, die die InvokeHandler -Schnittstelle implementiert. Der dynamische Proxy überreichte den Ausführungsprozess jeder Methode an die Verarbeitungsmethode.
JDK Dynamic Proxy verlangt, dass der Proxy eine Schnittstelle sein muss, eine einfache Klasse kann dies nicht. Die von JDK Dynamic Proxy generierten Proxy -Klassen erben die Proxy -Klasse, und die Proxy -Klasse implementiert die gesamte Schnittstellenliste, in die Sie übergeben wurden. Daher kann der Typ an den Schnittstellentyp gegossen werden. Unten ist das Strukturdiagramm des Proxy.
Es ist ersichtlich, dass Proxy alle statischen Methoden sind. Wenn die Proxy -Klasse keine Schnittstelle implementiert, dann ist es der Proxy -Typ und hat keine Instanzmethoden.
Wenn Sie sich anschließen, müssen Sie natürlich eine Klasse stellten, die keine bestimmte Schnittstelle implementiert, und die Methoden dieser Klasse sind die gleichen wie die, die von anderen Schnittstellen definiert sind, und sie können mit Reflexion einfach implementiert werden.
Public Class DynamicProxy implementiert InvokeHandler {// Die Klasse, die Sie private targetClass -Zielklasse = NULL proxy möchten; // Initialisieren Sie diese Klasse Public DynamicProxy (Zielklasse Zielklasse) {this.targetClass = targetClass; } Public Object Invoke (Objektproxy, Methode Methode, Object [] args) {// Verwenden Sie Reflection, um die Klasse zu erhalten, die Sie proxy -method myMethod = targetClass.getClass (). myMethod.setAccessible (wahr); return myMethod.invoke (targetClass, args); }}4. JDK Dynamic Proxy Quellcode Analyse (JDK7)
Nachdem wir uns das obige Beispiel angesehen haben, wissen wir nur, wie man dynamischen Proxy verwendet. Es ist jedoch immer noch neblig, wie die Proxy -Klasse erstellt wird, die die Invoke -Methode usw. bezeichnete. Die folgende Analyse
1. Wie werden Proxy -Objekte erstellt?
Schauen Sie sich zunächst den Quellcode der Proxy.NewinStance -Methode an:
öffentliches statisches Objekt NewProxyInstance (Classloader Loader, Class <?> [] Schnittstellen, InvocationHandler H) löst IllegalArgumentException {} // Schnittstelleninformationen endgültig Class <> [] intfs = interfaces.clone () ab; Final SecurityManager sm = System.getSecurityManager (); if (sm! } // Proxy Class Class <?> Cl = getProxyClass0 (Loader, INTFS) erstellen; // ... ok, schauen wir uns die erste Hälfte zuerst an}Aus dem Quellcode ist ersichtlich, dass die Erzeugung von Proxy -Klassen von der GetProxyClass0 -Methode abhängt. Schauen wir uns als nächstes den Quellcode getProxyClass0 an:
private statische Klasse <?> getProxyClass0 (Classloader Loader, Class <?> ... Schnittstellen) {// Die Anzahl der Schnittstellenlisten darf 0xffff nicht überschreiten, wenn (Interfaces.length> 65535) {neue IllegalArgumentException ("Schnittstellengrenze überschreitet"); } // Beachten Sie hier, dass die folgende Erläuterung im Detail zur Rückgabe von Proxyclasscache.get (Loader, Schnittstellen) angegeben ist. } Die Erläuterung von Proxyclasscache.get lautet: Wenn die Proxy -Klasse, die die Schnittstellenliste implementiert, bereits vorhanden ist, nehmen Sie sie direkt aus dem Cache. Wenn es nicht existiert, wird man durch ProxyClassFactory erzeugt.
Bevor Sie sich den Quellcode von Proxyclasscache ansehen.
private statische endgültige WeaPecache <Classloader, Klasse <?> [], Klasse <? >> proxyclassCache = new Weapcache <> (new KeyFactory (), New ProxyClassFactory ());
Proxyclasscache ist ein Cache vom Typ Weakcache -Typ. Sein Konstruktor hat zwei Parameter. Einer von ihnen ist der ProxyClassFactory, mit dem die Proxy -Klasse generiert wird. Das Folgende ist der Quellcode von Proxyclasscache.get:
endgültige Klasse Weapcache <k, p, v> {... public v get (k key, p parameter) {}}Hier repräsentiert K Key, P repräsentiert Parameter, V repräsentiert Wert
public v get (k key, p parameter) {// java7 nullObject urteilenmethode, wenn der Parameter leer ist, wird eine Ausnahme mit der angegebenen Nachricht geworfen. Wenn nicht leer, kehren Sie zurück. Objekte.Requirenonnull (Parameter); // Die Datenstruktur von WeaChashMap, die schwache Referenzen hält, die im Allgemeinen zum zwischenstrahlenden Abschluss von Abamse () verwendet werden; // CacheKey aus dem Queue -Objekt cachekey = cachekey.valueof (Schlüssel, RefQueue) abrufen; // Füllen Sie den Lieferanten mit faulem Laden aus. Concurrent ist eine Thread-Safe-Karte ConcurrentMap <Objekt, Lieferant <v >> valuesmap = map.get (cachekey); if (valuesmap == null) {ConcurrentMap <Objekt, Lieferant <v >> oldValuesMap = map.putiFabSent (cachekey, valuesmap = new ConcurrentHasMap <> ()); if (oldValuesMap! = null) {valuesMap = oldValuesMap; }} // subkey erstellen und den möglichen Lieferanten <V> abrufen, der von diesem // subkey aus VALTURSMAP -Objekt subKey = Objects.requirenonnull (subKeyFactory.Apply (Schlüssel, Parameter)) abgerufen wird; Lieferant <V> Lieferant = valuesmap.get (subKey); Werksfabrik = NULL; while (true) {if (Lieferant! = null) {// Wert von Lieferanten erhalten. Dieser Wert kann eine Fabrik- oder Cache -Realisierung sein. // Die folgenden drei Sätze sind der Kerncode, der die Klasse zurückgibt, die InvokeHandler implementiert und die erforderlichen Informationen enthält. V value = Lieferant.get (); if (value! = null) {Rückgabewert; } } // else no supplier in cache // or a supplier that returned null (can be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) //The following process is the process of filling the supplier if(factory == null) { //Create a factory } if(supplier == null) { //Fill supply }else { //Fill supply } } Die Funktion von während der Schleife besteht darin, die Klasse, die InvokeHandler implementiert, kontinuierlich zu erhalten. Diese Klasse kann aus dem Cache erhalten oder aus ProxyfaktoryClass generiert werden.
Die Fabrik ist eine interne Klasse, die die Lieferant <V. -Schnittstelle implementiert. Diese Klasse überschreibt die GET -Methode, und eine Instanzmethode des Type ProxyfactoryClass wird in der GET -Methode aufgerufen. Diese Methode ist die eigentliche Möglichkeit, eine Proxy -Klasse zu erstellen. Sehen wir uns den Quellcode der ProxyfactoryClass#anwenden -Methode an:
public class <??> apply (classloader loader, class <?> [] interfaces) {map <class <?>, boolean> interfaceset = new IdentityHasMap <> (interfaces.length); für (Klasse <?> Intf: Schnittstellen) { /* Überprüfen Sie, ob der Klasse Loader den Namen dieser Schnittstelle zu demselben Klassenobjekt auflöst.* / class <?> interfaceClass = null; Versuchen Sie es mit {// Informationen zu jedem interfaceClass = class.Forname (intf.getName (), false, Loader); } catch (classNotFoundException e) {} // Wenn die mit Ihrer eigene Klasse geladene Klasse nicht der Klasse entspricht, in der Sie bestanden haben, werfen Sie eine Ausnahme, wenn (interfaceClass! } // Wenn das Einbruch kein Schnittstellentyp ist, wenn (! InterfaceClass.Isersinterface ()) {neue illegalArgumentException (interfaceClass.getName () + "ist keine Schnittstelle"); } // Überprüfen Sie, ob die Schnittstelle wiederholt wird, wenn (interfaceset.put (interfaceClass, boolean.true)! }} String proxypkg = null; // Paket zum Definieren der Proxy-Klasse in /* Zeichnen Sie das Paket einer nicht öffentlichen Proxy-Schnittstelle auf, sodass die Proxy-Klasse im selben Paket definiert wird. * Überprüfen Sie, ob sich alle nicht öffentlichen Proxy-Schnittstellen im selben Paket befinden. */// Dieser Absatz hängt davon ab, ob es Schnittstellen gibt, die in der Schnittstelle, in die Sie bestanden haben, nicht öffentlich sind. Wenn ja, müssen alle diese Schnittstellen in einem Paket definiert werden. Ansonsten werfen Sie Ausnahmen für (Klasse <?> Intf: interfaces) {int flags = intf.getModifiers (); if (! modifier.ispublic (Flags)) {String name = intf.getName (); int n = name.lastIndexof ('.'); String pkg = ((n == -1)? "": Name.substring (0, n + 1)); if (proxypkg == null) {proxypkg = pkg; } else if (! pkg.equals (proxypkg)) {werfen Sie neue illegalArgumentException ("nicht öffentliche Schnittstellen aus verschiedenen Paketen"); }}}} if (proxypkg == null) {// Wenn keine nicht öffentlichen Proxy-Schnittstellen, verwenden Sie com.sun.proxy paket proxypkg = reflectil.proxy_package + ". } / * * Wählen Sie einen Namen, damit die Proxy -Klasse generiert werden kann. */ long num = nextUniqueNumber.getandIncrement (); // generieren Sie den Klassennamen der zufälligen Proxy -Klasse, $ proxy + num string proxyname = proxypkg + proxyclassnameprefix + num; /** Generieren Sie die Klassendatei der Proxy -Klasse, geben Sie den Byte -Stream zurück*/ byte [] proxyclassfile = proxygenerator.generateProxyclass (Proxyname, Schnittstellen); try {return defeclass0 (Loader, Proxyname, ProxyClassfile, 0, proxyClassfile.length); } catch (classformaterror e) {// End -Wurf neu IllegalArgumentException (e.toString ()); }}}Die oben erwähnte ProxyfactoryClass#Apply ist eine Methode, um wirklich Proxyklassen zu generieren, was tatsächlich ungenau ist. Nachdem wir den Quellcode hier gelesen haben, werden wir feststellen, dass der Proxygenerator#GenerateProxyClass die Methode ist, um wirklich Proxy -Klassen zu generieren. Generieren Sie die entsprechende Klassendatei gemäß der Java -Klasse -Bytecode -Komposition (siehe meinen anderen Artikel Java Bytecode Learning Notes). Der spezifische Quellcode von Proxygenerator#GenerateProxyClass ist wie folgt:
private byte [] GenerateClassfile () { / * * Schritt 1: Proxymethod -Objekte für alle Methoden zusammenstellen, um den Proxy -Versandcode für zu generieren. */ // AddProxymethod -Methode besteht darin, alle Methoden einer Liste hinzuzufügen und der entsprechenden Klasse // zu entsprechen, die drei Methoden entsprechen, die dem Objekt entsprechen, das Tastring und gleich AddProxymethod (Hashcodemethod, Object.class); AddProxymethod (EqualsMethod, Object.Class); AddProxymethod (toStringMethod, Object.class); // Vergleichen Sie die Schnittstelle in der Schnittstellenliste mit den Methoden unter der Schnittstelle für (int i = 0; i <interfaces.length; i ++) {method [] methods = interfaces [i] .getMethods (); für (int j = 0; j <methods.length; j ++) {AddProxymethod (Methoden [j], Schnittstellen [i]); }} / * * Für jeden Satz von Proxy -Methoden mit derselben Signatur * Stellen Sie sicher, dass die Rückgabetypen der Methoden kompatibel sind. */ for (list <Proxymethod> signmeThods: proxymethods.values ()) {checkReturntypes (sigmethods); } / * * Schritt 2: Montage FieldInfo- und MethodInfo -Strukturen für alle * Felder und Methoden in der Klasse, die wir generieren. *// Fügen Sie der Methode eine Konstruktormethode hinzu, bei der es sich nur um einen Konstruktor handelt. Dies ist ein Konstruktor mit der InvocationHandler -Schnittstelle. Es wurde jedoch noch nicht verarbeitet. Es wird gerade zuerst hinzugefügt und warte auf die Schleife. Die Namensbeschreibung des Konstruktors in der Klassendatei ist <In init> try {methods.add (GenerateConstructor ()); für (list <Proxymethod> signMethods: proxymethods.values ()) {for (proxymethod pm: signMethods) {// Fügen Sie jeder Proxy -Methode ein Attribut für Methodenart hinzu. Die Nummer 10 ist die Kennung der Klassendatei, was bedeutet, dass diese Attribute Fields.add (neues FieldInfo (PM.MethodfieldName, "Ljava/Lang/Reflect/Methode;", ACC_PRIVATE | ACC_STATION) sind. // Fügen Sie jede Proxy -Methode zu den Proxy -Klasse -Methoden (PM.Generatemethod ()) hinzu; }} // fügen Sie einen statischen Initialisierungsblock hinzu und initialisieren Sie jedes Attribut. Hier wird der statische Codeblock auch als Klassenkonstruktor bezeichnet. Es handelt sich tatsächlich um eine Methode mit dem Namen <Clinit>. Fügen Sie sie also den Methodenlistenmethoden hinzu (GeneratestaticInitializer ()). } catch (ioException e) {neue InternalError werfen ("unerwartete I/O -Ausnahme"); } // Die Anzahl der Methoden und Attribute darf 65535 einschließlich der vorherigen Anzahl der Schnittstellen nicht überschreiten. // Dies liegt daran, dass in der Klassendatei diese Zahlen in 4 -Bit -Hexadezimal dargestellt werden. Daher beträgt der Maximalwert 2 für die Leistung von 16. -1. Wenn (methods.SIZE ()> 65535) {neue illegalArgumentException ("Methodenlimit überschritten"); } if (fields.size ()> 65535) {werfen neuer illegalArgumentException ("Feldgrenze überschritten"); } // Der nächste Schritt besteht darin, eine Klassendatei mit magischen Nummern, Klassennamen, konstanten Pools und anderen Bytecodes -Reihe zu schreiben. Ich werde nicht auf Details eingehen. Wenn Sie es benötigen, können Sie sich auf das relevante Wissen von JVM Virtual Machine -Bytecode beziehen. CP.GetClass (Dottoslash (Klassenname)); cp.getClass (SuperClassName); für (int i = 0; i <interfaces.length; i ++) {cp.getClass (Dottoslash (Schnittstellen [i] .getName ()); } cp.setReadOnly (); BytearrayoutputStream bout = new bytearrayoutputStream (); DataOutputStream dout = new DataOutputStream (bout); Versuchen Sie {// u4 Magic; dout.writeInt (0xcafebabe); // u2 Minor_version; dout.writeshort (classFile_minor_version); // U2 major_version; dout.writeshort (classFile_major_version); CP.Write (Dout); // (Schreiben Sie konstantem Pool) // u2 access_flags; dout.writeshort (ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // u2 this_class; dout.writeshort (cp.getClass (dottoslash (className))); // U2 Super_Class; dout.writeshort (cp.getClass (SuperClassName)); // u2 interfaces_count; dout.writeshort (interfaces.length); // u2 interfaces [interfaces_count]; für (int i = 0; i <interfaces.length; i ++) {dout.writeshort (cp.getClass (dottoslash (interfaces [i] .getName ())); } // u2 fields_count; Dout.Writeshort (fields.size ()); // field_info fields [fields_count]; für (fieldInfo f: fields) {f.write (dout); } // u2 methody_count; dout.writeshort (methods.size ()); // method_info methods [methoden_count]; für (methodInfo m: methoden) {M.Write (Dout); } // U2 Attribute_Count; Dout.Writeshort (0); // (keine Class -Datei -Attribute für Proxy -Klassen)} catch (ioException e) {Neue InternalError werfen ("unerwartete I/O -Ausnahme"); } return bout.tobytearray (); }Nach Aufrufen von Schichten wird schließlich eine Proxy -Klasse generiert.
2. Wer hat Invoke angerufen?
Wir simulieren JDK, um selbst eine Proxy -Klasse zu generieren. Der Klassenname ist TestProxygen:
public class testgeneratorProxy {public static void main (String [] args) löst ioException {byte [] classFile = proxygenerator.generateProxyClass ("testproxygen", Subjekt.class.getInterfaces ()) aus; Datei Datei = neue Datei ("/user/yadoao/desktop/testproxygen.class"); FileOutputStream fos = new FileOutputStream (Datei); fos.write (classfile); fos.flush (); fos.close (); }}Dekompilieren Sie die Klassendatei mit JD-GUI, und das Ergebnis lautet wie folgt:
import com.su.dynamicproxy.isubject; import Java.lang.reflect.invocationHandler; import Java.lang.reflect.method; import Java.lang.reflect.Proxy; private statische Methode M1; private statische Methode M0; private statische Methode M4; private statische Methode M2; public testProxygen (invocationHandler paramInvocationHandler) löscht {Super (paramInvocationHandler) aus; } public Final void sayshello () löst {try {this.h.invoke (this, m3, null); zurückkehren; } catch (Fehler | RunTimeException localError) {localError werfen; } catch (Throwable Local throwable) {werfen neu ohne offenkundigem Drowablexception (Local throwable); }} public Final Boolean Equals (Object ParamObject) löscht {try {return ((boolean) this.h.invoke (this, m1, neues Objekt [] {paramobject})). booleanValue (); } catch (Fehler | RunTimeException localError) {localError werfen; } catch (Throwable Local throwable) {werfen neu ohne offenkundigem Drowablexception (Local throwable); }} public Final int hashCode () löscht {try {return ((integer) this.h.invoke (this, m0, null)). intValue (); } catch (Fehler | RunTimeException localError) {localError werfen; } catch (Throwable Local throwable) {werfen neu ohne offenkundigem Drowablexception (Local throwable); }} public Final void sagtgoodbye () löst {try {this.h.invoke (this, m4, null); zurückkehren; } catch (Fehler | RunTimeException localError) {localError werfen; } catch (Throwable Local throwable) {werfen neu ohne offenkundigem Drowablexception (Local throwable); }} public Final String toString () löscht {try {return (string) this.h.invoke (this, m2, null); } catch (Fehler | RunTimeException localError) {localError werfen; } catch (Throwable Local throwable) {werfen neu ohne offenkundigem Drowablexception (Local throwable); }} static {try {m3 = class.forname ("com.su.dynamicproxy.isubject"). getMethod ("SayHello", neue Klasse [0]); m1 = class.forname ("java.lang.Object"). getMethod ("Equals", New Class [] {class.forname ("java.lang.object")}); M0 = class.Forname ("java.lang.object"). getMethod ("HashCode", neue Klasse [0]); M4 = class.Forname ("com.su.dynamicproxy.isubject"). GetMethod ("Saygoodbye", neue Klasse [0]); m2 = class.Forname ("java.lang.object"). getMethod ("tostring", neue Klasse [0]); zurückkehren; } catch (NoSuchMethodException localnosuchMethodException) {neue noSuchMethoderror werfen (localnosuchMethodException.getMessage ()); } catch (classNotFoundException localClassNotFoundException) {neue noclassDeffoundError werfen (localClassNotFoundException.getMessage ()); }}} Zunächst bemerkte ich, dass der Konstruktor der generierten Proxy -Klasse in einer Klasse übergeben wird, die die InvokeHandler -Schnittstelle als Parameter implementiert, und als Konstruktor der übergeordneten Klasse -Proxy bezeichnet, die die von der Mitgliedsvariablen geschützte InvokeHander H in Proxy initialisierte.
Ich bemerkte wieder mehrere statische Initialisierungsblöcke. Der statische Initialisierungsblock besteht hier darin, die Proxy -Schnittstellenliste und Hashcode, ToString und gleiche Methoden zu initialisieren.
Schließlich gibt es den aufrufenden Prozess dieser Methoden, die alle Rückrufe zur Invoke -Methode sind.
Dies endet mit der Analyse dieses Proxy -Musters.