Durch die Analyse der vorherigen Artikel wissen wir, dass die Proxy -Klasse über die ProxyclassFactory -Fabrik der Proxy -Klasse generiert wird. Diese Fabrikklasse nennt die Methode GenerateProxyClass () der Proxygenerator -Klasse, um die Bytecode der Proxy -Klasse zu generieren. Die Proxygenerator -Klasse wird im Sun.misc -Paket gespeichert. Wir können diese Klasse über den OpenJDK -Quellcode finden. Der Kerninhalt der statischen Methode generatedProxyClass () dieser Klasse besteht darin, die Instanzmethode generatedClassFile () aufzurufen, um Klassendateien zu generieren. Schauen wir uns an, was innerhalb der Methode für GenerateClassfile () gemacht wird.
private byte [] GenerateClassfile () {// Der erste Schritt besteht darin, alle Methoden in Proxymethod -Objekte zusammenzustellen // Erstellen Sie zunächst Proxy -Methoden wie ToString, HashCode, Equals usw. AddProxymethod (Hashcodemethod, Object.class); AddProxymethod (EqualsMethod, Object.Class); AddProxymethod (toStringMethod, Object.class); // Übertragen Sie jede Methode jeder Schnittstelle und generieren Sie ein Proxymethod -Objekt 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 Proxy -Methoden mit derselben Signatur prüfen Sie, ob der Rückgabewert der Methode für (Liste <Proxymethod> Signaturen: proxymethods.values ()) {achteReturntypes (SigMethods) kompatibel ist. } // Schritt 2, alle Feldinformationen und Methodeninformationen der zu generierten Klassendatei zusammenstellen, {// die Konstruktormethoden hinzufügen (GenerateConstructor ()); // Übertragen Sie die Proxy -Methode in den Cache für (Liste <Proxymethod> SignMethods: Proxymethods.values ()) {for (Proxymethod PM: SignMethods) {// Statische Felder der Proxy -Klasse hinzufügen: private statische Methode M1; fields.add (neues FieldInfo (PM.MethodfieldName, "ljava/lang/reflektiert/methode;", ACC_Private | ACC_static)); // Proxy -Methoden der Proxy -Klasse -Methoden hinzufügen (pm.generatemethod ()); }} // statische Feldinitialisierungsmethoden hinzufügen (generatestaticInitializer ()); } catch (ioException e) {neue InternalError werfen ("unerwartete I/O -Ausnahme"); } // Die Verifizierungsmethode und die Feldsammlung können nicht größer als 65535 sein, wenn (methods.SIZE ()> 65535) {neue IllegalArgumentException ("Methodenlimit überschritten"); } if (fields.size ()> 65535) {werfen neuer illegalArgumentException ("Feldgrenze überschritten"); } if (fields.size ()> 65535) {werfen neuer illegalArgumentException ("Feldgrenze überschritten"); } // Schritt 3 schreiben Sie in die endgültige Klassendatei // überprüfen Sie, ob der voll qualifizierte Name der Proxy -Klasse in der konstanten Pool cp.getClass (Dottoslash (KlasseName)) vorhanden ist. // Überprüfen Sie, ob sich der voll qualifizierte Name der übergeordneten Klasse der Proxy -Klasse im konstanten Pool befindet, und der Name der Elternklasse lautet: "Java/Lang/Reflect/Proxy" cp.getClass (SuperClassName); // Überprüfen Sie, ob der vollständige qualifizierte Name der Proxy -Klasse -Schnittstelle für (int i = 0; i <interfaces.length; i ++) {cp.getClass (dottoslash (interfaces [i] .getName ())); } // Als nächstes starten Sie die Datei, die den konstanten Pool auf nur cp.setReadOnly () lesen; BytearrayoutputStream bout = new bytearrayoutputStream (); DataOutputStream dout = new DataOutputStream (bout); Versuchen Sie {// 1. Schreiben Sie in die magische Nummer dout.writeInt (0xcafebabe); // 2. Schreiben Sie in die sekundäre Versionsnummer Dout.WriteSeshort (classFile_Minor_version); // 3. Schreiben Sie in die Hauptversionsnummer dout.writeshort (classFile_major_version); // 4. Schreiben Sie in den konstanten Pool cp.write (Dout); // 5. Schreibzugriffsmodifikator dout.writeshort (ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // 6. Schreibklassenindex dout.Writeshort (cp.getClass (dottoslash (className))); // 7. Schreiben Sie den übergeordneten Klassenindex, die generierten Proxy -Klassen werden von Proxy Dout.WriteShort (CP.GetClass (SuperClassName)) geerbt. // 8. STRECT -VERFACT VALUE DOUT.WRITESHORT (interfaces.length); // 9. Schnittstelle für (int i = 0; i <interfaces.length; i ++) {dout.writeshort (cp.getClass (dottoslash (interfaces [i] .getName ()))); } // 10. Schreibfeldzahl Wert Dout.Writeshort (Fields.Size ()); // 11. Schreibfeldsammlung für (fieldInfo f: fields) {f.write (dout); } // 12. Write Method Count Value Dout.Writeshort (methods.Size ()); // 13. Schreibmethodensammlung für (methodInfo m: methody) {M.Write (Dout); } // 14. Schreiben Sie den Eigenschaftszahlwert, die Proxy -Klassendatei hat keine Attribute, daher ist sie 0 Dout.Writeshort (0); } catch (ioException e) {neue InternalError werfen ("unerwartete I/O -Ausnahme"); } // in Binary Array umgebaut, um zurückzusetzen.Sie können sehen, dass die Methode für GenerateClassfile () gemäß der Klassendateistruktur dynamisch gespleißt wird. Was ist eine Klassendatei? Hier werden wir zunächst erklären, dass die Java -Datei, die wir normalerweise schreiben, mit .java endet. Kompilieren Sie es nach dem Schreiben durch den Compiler und generieren Sie eine .class -Datei. Diese .class -Datei ist eine Klassendatei. Die Ausführung von Java -Programmen hängt nur von Klassendateien ab und hat nichts mit Java -Dateien zu tun. Diese Klassendatei beschreibt die Informationen einer Klasse. Wenn wir eine Klasse verwenden müssen, lädt die java virtuelle Maschine die Klassendatei dieser Klasse im Voraus und führt die Initialisierung und die damit verbundene Überprüfung durch. Die virtuelle Java -Maschine kann sicherstellen, dass diese Aufgaben erledigt werden, bevor Sie diese Klasse verwenden. Wir müssen es nur mit Seelenfrieden verwenden, ohne sich darum zu kümmern, wie die java -virtuelle Maschine es lädt. Natürlich müssen Klassendateien nicht unbedingt durch Kompilieren von Java -Dateien kompiliert werden. Sie können sogar Klassendateien direkt über einen Texteditor schreiben. Hier generiert der JDK Dynamic Proxy Klassendateien über Programme. Kehren wir wieder zum obigen Code zurück und sehen, dass das Generieren der Klassendatei hauptsächlich in drei Schritte unterteilt ist:
Schritt 1: Sammeln Sie alle zu erzeugenden Proxy -Methoden, wickeln Sie sie in Proxymethod -Objekte ein und registrieren Sie sie in die Kartensammlung.
Schritt 2: Sammeln Sie alle Feldinformationen und Methodeninformationen, die für die Klassendatei generiert werden sollen.
Schritt 3: Beginnen Sie nach Abschluss der oben genannten Arbeiten die Klassendatei zusammen.
Wir wissen, dass der Kernteil einer Klasse ihre Felder und Methoden sind. Konzentrieren wir uns auf den zweiten Schritt, um zu sehen, welche Felder und Methoden es für die Proxy -Klasse generiert. Im zweiten Schritt wurden die folgenden vier Dinge in der Reihenfolge erledigt.
1. Generieren Sie einen Parameterkonstruktor für die Proxy -Klasse, geben Sie einen Verweis auf die InvocationHandler -Instanz über und rufen Sie den Parameterkonstruktor der übergeordneten Klasse auf.
2. Iterieren Sie die Kartensammlung von Proxy -Methoden, generieren Sie die entsprechende statische Domäne des entsprechenden Methodentyps für jede Proxy -Methode und fügen Sie sie der Fields -Sammlung hinzu.
3. Iterieren Sie die Kartensammlung von Proxy -Methoden, generieren Sie das entsprechende Methodinfo -Objekt für jede Proxy -Methode und fügen Sie es der Methodensammlung hinzu.
4. Generieren Sie eine statische Initialisierungsmethode für die Proxy -Klasse. Diese statische Initialisierungsmethode weist hauptsächlich die Referenz jeder Proxy -Methode dem entsprechenden statischen Feld zu.
Durch die obige Analyse können wir grob wissen, dass JDK Dynamic Proxy irgendwann eine Proxy -Klasse mit der folgenden Struktur für uns erzeugen wird:
public class Proxy0 erweitert Proxy implementiert UserDao {// Schritt 1, generieren Konstruktor -proxy0 (InvocationHandler H) {Super (h); } // Schritt 2, generieren Sie die private statische Methode der statischen Domäne M1; // HashCode -Methode private statische Methode M2; // Equals Methode Private statische Methode M3; // ToString -Methode private statische Methode M4; // ... // Schritt 3, Proxy -Methode @Override public int HashCode () {try {return (int) h.invoke (this, m1, null) erstellen; } catch (throwable e) {werfen Sie neue nicht deklarierte Drüsenverfeinerung (e); }} @Override public boolean Equals (Objekt obj) {try {Object [] args = new Object [] {obj}; return (boolean) h.invoke (this, m2, args); } catch (throwable e) {werfen Sie neue nicht deklarierte Drüsenverfeinerung (e); }} @Override public String toString () {try {return (string) h.invoke (this, m3, null); } catch (throwable e) {werfen Sie neue nicht deklarierte Drüsenverfeinerung (e); }} @Override public void Save (Benutzerbenutzer) {try {// Konstruieren Sie das Parameter -Array, wenn später mehrere Parameter hinzugefügt werden, nur Object [] args = new Object [] {Benutzer}; H.Invoke (this, m4, args); } catch (throwable e) {werfen Sie neue nicht deklarierte Drüsenverfeinerung (e); }} // Schritt 4, generieren Sie die statische Initialisierungsmethode statisch {try {class c1 = class.forname (Object.class.getName ()); Klasse c2 = class.forname (userDao.class.getName ()); M1 = c1.getMethod ("HashCode", null); m2 = c1.getMethod ("Equals", New Class [] {Object.class}); M3 = c1.getMethod ("toString", null); m4 = c2.getMethod ("speichern", neue Klasse [] {user.class}); // ...} catch (Ausnahme e) {e.printstacktrace (); }}}Zu diesem Zeitpunkt haben wir nach der geschichteten Analyse und einer eingehenden Erforschung des JDK-Quellcodes das ursprüngliche Erscheinungsbild der dynamisch generierten Proxy-Klasse wiederhergestellt, und einige der vorherigen Fragen wurden ebenfalls gut erklärt.
1. Die Proxy -Klasse erbt standardmäßig die Porxy -Klasse. Da Java nur die einzelne Vererbung unterstützt, kann JDK Dynamic Proxy nur Schnittstellen implementieren.
2. Die Proxy -Methoden rufen die Invoke () -Methode des InvocationHandlers auf, sodass wir die Invoke () -Methode des InvocationHandlers neu schreiben müssen.
3. Bei Aufrufen der Invoke () -Methode werden die Proxy -Instanz selbst, die Zielmethode und die Parameter der Zielmethode übergeben. Erklären Sie, wie die Parameter der Invoke () -Methode ausgehen.
Verwenden Sie den neu konstruierten Proxy0 als Proxy -Klasse, um erneut zu testen, und Sie können sehen, dass das Endergebnis mit der mit JDK dynamischen Erzeugung generierten Proxy -Klasse übereinstimmt. Wiederum ist unsere Analyse zuverlässig und genau. Zu diesem Zeitpunkt wurden die Artikel der JDK Dynamic Proxy Series angekündigt. Durch die Analyse dieser Serie hat der Autor die langjährigen Zweifel in seinem Herzen gelöst, und ich glaube, dass das Verständnis der Leser für JDK Dynamic Proxy noch einen Schritt weiter geworden ist. Das Wissen über Papier ist jedoch immer flach. Wenn Sie die JDK -Dynamik -Proxy -Technologie besser beherrschen möchten, können die Leser auf diese Artikelserie verweisen, um den JDK -Quellcode selbst zu überprüfen oder Lernerfahrung mit dem Autor auszutauschen, auf die unangemessene Analyse des Autors zu verweisen, zusammen zu lernen und gemeinsam Fortschritte zu machen.
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.