Melalui analisis artikel sebelumnya, kita tahu bahwa kelas proxy dihasilkan melalui pabrik proxyclassfactory dari kelas proxy. Kelas pabrik ini akan memanggil metode generateProxyClass () dari kelas proxygenerator untuk menghasilkan bytecode kelas proxy. Kelas proxygenerator disimpan dalam paket Sun.misc. Kami dapat menemukan kelas ini melalui kode sumber OpenJDK. Konten inti dari metode statis yang dihasilkanProxyClass () dari kelas ini adalah untuk memanggil metode instance yang dihasilkanClassFile () untuk menghasilkan file kelas. Mari kita lihat apa yang dilakukan di dalam metode generateClassFile ().
byte pribadi [] generateClassFile () {// Langkah pertama adalah merakit semua metode menjadi objek proxymethod // pertama -tama menghasilkan metode proxy seperti tostring, hashcode, setara, dll. AddProxyMethod (hashcodemethod, objek.class); addProxyMethod (EqualsMethod, Object.class); addproxymethod (TostringMethod, Object.class); // Transfer setiap metode dari setiap antarmuka dan hasilkan objek proxymethod untuk itu untuk (int i = 0; i <antarmuka. untuk (int j = 0; j <methods.length; j ++) {addProxyMethod (Metode [j], antarmuka [i]); }} // Untuk metode proxy dengan tanda tangan yang sama, periksa apakah nilai pengembalian metode ini kompatibel untuk (daftar <proxymethod> tanda tangan: proxymethods.values ()) {checkReturnTypes (sigmethods); } // Langkah 2, rakit semua informasi bidang dan informasi metode file kelas yang akan dihasilkan coba {// tambahkan metode konstruktor metode. // Transfer metode proxy dalam cache untuk (daftar <proxymethod> SignMethods: proxymethods.values ()) {for (Proxymethod PM: SignMethods) {// Tambahkan bidang statis dari kelas proxy, misalnya: Metode statis pribadi M1; fields.add (fieldInfo baru (pm.methodfieldname, "ljava/lang/reflect/method;", acc_private | acc_static)); // tambahkan metode proxy dari metode kelas proxy.add (pm.generatemethod ()); }} // Tambahkan metode inisialisasi bidang statis. Metode. } catch (ioException e) {lempar internalError baru ("Pengecualian I/O yang tidak terduga"); } // Metode verifikasi dan pengumpulan lapangan tidak boleh lebih besar dari 65535 if (methods.size ()> 65535) {lempar baru ilegalargumentException ("Batas metode terlampaui"); } if (fields.size ()> 65535) {lempar baru ilegalargumentException ("batas lapangan terlampaui"); } if (fields.size ()> 65535) {lempar baru ilegalargumentException ("batas lapangan terlampaui"); } // Langkah 3, tulis ke file kelas akhir // Pastikan bahwa ada nama yang sepenuhnya memenuhi syarat dari kelas proxy di pool cp.getClass konstan (dottoslash (classname)); // Pastikan bahwa ada nama yang sepenuhnya memenuhi syarat dari kelas induk kelas proxy di kumpulan konstan, dan nama kelas induk adalah: "java/lang/reflect/proxy" cp.getClass (superclassname); // Verifikasi bahwa nama penuh yang memenuhi syarat dari antarmuka kelas proxy untuk (int i = 0; i <antarmuka. } // Selanjutnya untuk mulai menulis file, atur kumpulan konstan untuk hanya membaca cp.setreadonly (); BytearrayoutputStream bout = new bytearrayoutputStream (); DataOutputStream dout = DataOutputStream baru (Bout); coba {// 1. Tulis ke Magic Number Dout.writeint (0xCafeBabe); // 2. Tulis ke nomor versi sekunder dout.writeshort (classfile_minor_version); // 3. Tulis ke nomor versi utama dout.writeshort (classfile_major_version); // 4. Tulis ke Pool CP.Write (Dout) yang konstan; // 5. Write Access Modifier Dout.WriteShort (ACC_PUBLIC | ACC_Final | ACC_SUPER); // 6. Write Class Index Dout.WriteShort (cp.getClass (dottoslash (className))); // 7. Tulis indeks kelas induk, kelas proxy yang dihasilkan diwarisi dari proxy dout.writeshort (cp.getClass (superclassName)); // 8. Write Interface Count Value Dout.WriteShort (Interfaces.Length); // 9. Tulis Antarmuka Set untuk (int i = 0; i <interfaces.length; i ++) {dout.writeShort (cp.getClass (dottoslash (antarmuka [i] .getName ()))); } // 10. Tulis Nilai Hitungan Bidang Dout.WriteShort (fields.size ()); // 11. Tulis Koleksi Bidang untuk (FieldInfo F: Fields) {f.write (dout); } // 12. Metode Tulis Hitungan Nilai Dout.WriteShort (Methods.Size ()); // 13. Tulis Metode Koleksi untuk (MethodInfo M: Metode) {M.Write (Dout); } // 14. Tulis nilai jumlah properti, file kelas proxy tidak memiliki atribut, jadi itu 0 dout.writeshort (0); } catch (ioException e) {lempar internalError baru ("Pengecualian I/O yang tidak terduga"); } // konversi ke array biner ke output return bout.tobytearray ();}Anda dapat melihat bahwa metode generateClassFile () secara dinamis disambung sesuai dengan struktur file kelas. Apa itu file kelas? Di sini pertama -tama kita akan menjelaskan bahwa file java yang biasanya kita tulis berakhir dengan .java. Setelah menulisnya, kompilasi melalui kompiler dan buat file .class. File .class ini adalah file kelas. Eksekusi program Java hanya tergantung pada file kelas dan tidak ada hubungannya dengan file Java. File kelas ini menjelaskan informasi kelas. Ketika kita perlu menggunakan kelas, mesin virtual Java akan memuat file kelas kelas ini terlebih dahulu dan melakukan inisialisasi dan verifikasi terkait. Mesin virtual Java dapat memastikan bahwa tugas -tugas ini akan diselesaikan sebelum Anda menggunakan kelas ini. Kita hanya perlu menggunakannya dengan ketenangan pikiran, tanpa peduli tentang bagaimana mesin virtual Java memuatnya. Tentu saja, file kelas tidak harus dikompilasi dengan menyusun file java. Anda bahkan dapat menulis file kelas secara langsung melalui editor teks. Di sini, proxy dinamis JDK secara dinamis menghasilkan file kelas melalui program. Mari kita kembali ke kode di atas lagi dan lihat bahwa menghasilkan file kelas terutama dibagi menjadi tiga langkah:
Langkah 1: Kumpulkan semua metode proxy yang akan dihasilkan, bungkus ke dalam objek proxymethod dan daftarkan ke dalam koleksi peta.
Langkah 2: Kumpulkan semua informasi bidang dan informasi metode yang akan dihasilkan untuk file kelas.
Langkah 3: Setelah menyelesaikan pekerjaan di atas, mulailah menyusun file kelas.
Kita tahu bahwa bagian inti dari suatu kelas adalah bidang dan metodenya. Mari kita fokus pada langkah kedua untuk melihat bidang dan metode apa yang dihasilkannya untuk kelas proxy. Pada langkah kedua, empat hal berikut dilakukan secara berurutan.
1. Hasilkan konstruktor parameter untuk kelas proxy, lulus dalam referensi ke instance AvoCationHandler dan panggil konstruktor parameter dari kelas induk.
2. Iterasi di atas koleksi peta metode proxy, hasilkan domain statis tipe metode yang sesuai untuk setiap metode proxy, dan tambahkan ke koleksi Fields.
3. Iterasi di atas koleksi peta metode proxy, hasilkan objek MethodInfo yang sesuai untuk setiap metode proxy, dan tambahkan ke koleksi metode.
4. Hasilkan metode inisialisasi statis untuk kelas proxy. Metode inisialisasi statis ini terutama menetapkan referensi dari setiap metode proksi ke bidang statis yang sesuai.
Melalui analisis di atas, kita dapat secara kasar tahu bahwa proxy dinamis JDK pada akhirnya akan menghasilkan kelas proxy dengan struktur berikut untuk kita:
Public Class Proxy0 memperluas proxy mengimplementasikan userdao {// Langkah 1, menghasilkan konstruktor protected proxy0 (InvocationHandler h) {super (h); } // Langkah 2, Hasilkan Domain Statis Metode Statis Privat M1; // Metode HashCode Metode Statis Pribadi M2; // Equals Metode Metode Statis Pribadi M3; // metode tostring metode statis pribadi m4; // ... // Langkah 3, hasilkan metode proxy @Override public int hashCode () {coba {return (int) h.invoke (ini, m1, null); } catch (throwable e) {lempar baru undeclaredthrowableException (e); }} @Override public boolean sama (objek obj) {coba {objek [] args = objek baru [] {obj}; return (boolean) h.invoke (ini, m2, args); } catch (throwable e) {lempar baru undeclaredthrowableException (e); }} @Override Public String ToString () {coba {return (string) h.invoke (this, m3, null); } catch (throwable e) {lempar baru undeclaredthrowableException (e); }} @Override public void save (pengguna pengguna) {coba {// buat array parameter, jika ada beberapa parameter yang ditambahkan nanti, hanya objek [] args = objek baru [] {user}; H.Invoke (ini, M4, args); } catch (throwable e) {lempar baru undeclaredthrowableException (e); }} // Langkah 4, hasilkan metode inisialisasi statis statis {coba {class c1 = class.forname (object.class.getName ()); Class c2 = class.forname (userdao.class.getName ()); m1 = c1.getMethod ("hashcode", null); m2 = c1.getMethod ("sama", kelas baru [] {object.class}); m3 = c1.getMethod ("tostring", null); m4 = c2.getMethod ("simpan", kelas baru [] {user.class}); // ...} catch (Exception e) {E.PrintStackTrace (); }}}Pada titik ini, setelah analisis berlapis dan eksplorasi mendalam dari kode sumber JDK, kami memulihkan penampilan asli dari kelas proxy yang dihasilkan secara dinamis, dan beberapa pertanyaan sebelumnya juga dijelaskan dengan baik.
1. Kelas proxy mewarisi kelas Porxy secara default. Karena Java hanya mendukung warisan tunggal, proxy dinamis JDK hanya dapat mengimplementasikan antarmuka.
2. Metode proxy akan memanggil metode Invoke () dari InvocationHandler, jadi kita perlu menulis ulang metode Invoke () InvocationHandler.
3. Saat memanggil metode Invoke (), instance proxy itu sendiri, metode target dan parameter metode target akan diteruskan. Jelaskan bagaimana parameter metode Invoke () berasal.
Gunakan Proxy0 yang baru dibangun sebagai kelas proxy untuk menguji lagi, dan Anda dapat melihat bahwa hasil akhirnya sama dengan kelas proxy yang dihasilkan secara dinamis menggunakan JDK. Sekali lagi, analisis kami dapat diandalkan dan akurat. Pada titik ini, artikel seri proxy dinamis JDK telah diumumkan untuk mengakhiri. Melalui analisis seri ini, penulis telah memecahkan keraguan yang sudah lama ada di dalam hatinya, dan saya percaya bahwa pemahaman pembaca tentang proxy dinamis JDK telah menjadi satu langkah lebih jauh. Namun, pengetahuan di atas kertas selalu dangkal. Jika Anda ingin menguasai teknologi proxy dinamis JDK dengan lebih baik, pembaca dapat merujuk pada serangkaian artikel ini untuk memeriksa kode sumber JDK sendiri, atau bertukar pengalaman belajar dengan penulis, tunjukkan analisis penulis yang tidak pantas, pelajari bersama, dan buat kemajuan bersama.
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.