Kunci untuk mengimplementasikan proxy dinamis di Java adalah dua hal ini: proxy dan invocationHandler. Mari kita mulai dengan metode Invoke di antarmuka InvocationHandler dan jelaskan secara singkat bagaimana Java mengimplementasikan proxy dinamis.
Pertama, bentuk lengkap dari metode Invoke adalah sebagai berikut:
Invoke Objek Publik (Proxy Objek, Metode Metode, Objek [] args) melempar lempar {method.invoke (obj, args); return null;}Pertama, mari kita tebak metode itu adalah metode yang disebut, yaitu metode yang perlu dieksekusi; Args adalah parameter metode; Proxy, apa parameter ini? Implementasi metode Invoke () di atas adalah bentuk yang relatif standar. Kami melihat bahwa tidak ada parameter proxy yang digunakan di sini. Lihatlah deskripsi proxy dalam dokumentasi JDK, sebagai berikut:
Metode doa pada instance proxy melalui salah satu antarmuka proxy akan dikirim ke metode Invoke dari penangan doa instance, melewati instance proxy, java.lang.reflect.Method objek yang mengidentifikasi metode yang dipanggil, dan array objek tipe yang berisi argumen.
Dari sini kita dapat mengetahui bahwa tebakan di atas benar, dan kita juga tahu bahwa parameter proxy adalah instance dari kelas proxy.
Untuk kenyamanan penjelasan, berikut adalah contoh sederhana untuk mengimplementasikan proxy dinamis.
// Peran abstrak (proxy dinamis hanya dapat proxy antarmuka) subjek antarmuka publik {public void request (); } // Peran Nyata: Mengimplementasikan Metode Permintaan () Subjek Public Class RealSubject mengimplementasikan subjek {public void request () {System.out.println ("Dari Subjek Nyata."); }} // Menerapkan Kelas Publik InvocationHandler Dynamicsubject mengimplementasikan InvocationHandler {Private Object Obj; // Ini adalah manfaat dari proxy dinamis. Objek yang dienkapsulasi adalah tipe objek dan menerima objek dari jenis apa pun jenis public dynamicsubject () {} public dynamicsubject (objek obj) {this.obj = obj; } // Metode ini bukan apa yang kami tunjukkan untuk memanggil objek publik Invoke (objek proxy, metode metode, objek [] args) melempar lempar {system.out.println ("Sebelum memanggil" + metode); Method.invoke (OBJ, ARGS); System.out.println ("After Calling" + Metode); kembali nol; }} // Klien: Hasilkan instance proxy dan hubungi metode permintaan () Metode Public Class Client {public static void main (string [] args) melempar lemparan {// todo auto-method stub subjek rs = new realsubject (); // tentukan proxy class invocationHandler ds = new dinamicubject (rs); Kelas <?> Cls = rs.getClass (); // Berikut ini adalah generasi satu kali dari subjek subjek proxy = (subjek) proxy.newproxyInstance (cls.getClassLoader (), cls.getInterfaces (), ds); // Di sini Anda dapat membuktikan bahwa subjek adalah instance proxy dengan menjalankan hasil. Contoh ini mengimplementasikan antarmuka subjek System.out.println (instance subjek dari proxy); // Di sini Anda dapat melihat bahwa kelas kelas subjek adalah $ proxy0. Kelas $ proxy0 ini mewarisi proxy dan mengimplementasikan antarmuka subjek System.out.println ("Kelas kelas subjek adalah:"+subjek.getClass (). ToString ()); System.out.print ("Properti dalam subjek adalah:"); Bidang [] field = subjek.getClass (). GetDeclaredFields (); untuk (bidang f: bidang) {System.out.print (f.getName ()+","); } Metode dalam subjek System.out.print ("/n"+"adalah:"); Metode [] Method = Subject.getClass (). GetDeclaredMethods (); untuk (metode m: metode) {system.out.print (m.getName ()+","); } Kelas induk dari System.out.println ("/n"+"Subjek adalah:"+subjek.getClass (). GetSuperclass ()); System.out.print ("/n"+"Subjek mengimplementasikan antarmuka:"); Kelas <?> [] Antarmuka = subjek.getClass (). GetInterfaces (); untuk (kelas <?> i: antarmuka) {System.out.print (i.getName ()+","); } System.out.println ("/n/n"+"Hasil Jalankan adalah:"); Subject.Request (); }} Hasil operasi adalah sebagai berikut: Nama paket dihilangkan di sini, *** Sebaliknya
BENAR
Kelas kelas subjek adalah: kelas $ proxy0
Properti dalam subjek adalah: M1, M3, M0, M2,
Metode dalam subjek adalah: permintaan, kode hash, setara, tostring,
Kelas induk subjek adalah: kelas java.lang.reflect.proxy
Antarmuka yang diimplementasikan oleh subjek adalah: cn.edu.ustc.dynamicproxy.subject,
Hasil operasinya adalah:
Sebelum memanggil public abstract void ***. Subjek.Request ()
Dari subjek nyata.
Setelah memanggil public abstrak void ***. Subjek.Request ()
PS: Informasi tentang hasil ini sangat penting, setidaknya bagi saya. Karena akar penyebab pusing saya di proxy dinamis adalah bahwa saya salah paham dengan subjek di atas. Saya pernah bingung tentang bagaimana permintaan panggilan terakhir () terhubung ke Invoke (), dan bagaimana Invoke tahu bahwa permintaan itu ada. Faktanya, True dan Class $ Proxy0 di atas dapat menyelesaikan banyak pertanyaan, dan ditambah dengan kode sumber $ Proxy0 yang akan disebutkan di bawah ini, ia dapat sepenuhnya menyelesaikan keraguan proxy dinamis.
Dari kode dan hasil di atas, kita dapat melihat bahwa kita tidak menyebut metode Invoke () seperti yang ditunjukkan, tetapi metode ini memang dijalankan. Mari kita analisis seluruh proses di bawah ini:
Menilai dari kode di klien, Anda dapat menggunakan metode newproxyinstance sebagai terobosan. Mari pertama -tama lihat kode sumber metode newproxyinstance di kelas proxy:
Objek statis publik newProxyInstance (classloader loader, class <?> [] Antarmuka, InvocationHandler h) melempar IllegalArgumentException {if (h == null) {lempar nullPointerException baru (); } / * * Cari atau hasilkan kelas proxy yang dirancang. */ Class cl = getProxyClass (loader, antarmuka); / * * Memohon konstruktornya dengan penangan doa yang dirancang. * / coba { / * * Kode sumber proxy memiliki definisi berikut: * kelas statis akhir pribadi [] constructorparams = {InvocationHandler.class}; * Cons adalah metode konstruktor dengan parameter formal tipe InvocationHandler*/ constructor cons = cl.getConstructor (constructorparams); return (objek) cons.newinstance (objek baru [] {h}); } catch (nosuchmethodeException e) {lempar internalerror baru (e.toString ()); } catch (ilegalAccessException e) {lempar internalerror baru (e.toString ()); } catch (InstantiationException e) {lempar internalError baru (e.toString ()); } catch (InvocateTargetException e) {lempar internalError baru (e.toString ()); }} Proxy.newproxyInstance (classloader loader, class <?> [] Antarmuka, InvocationHandler h) melakukan hal -hal berikut.
(1) Panggil metode getProxyClass (loader, antarmuka) berdasarkan parameter loader dan antarmuka, buat kelas proxy $ proxy0. $ Proxy0 kelas, mengimplementasikan antarmuka antarmuka, dan mewarisi kelas proxy.
(2) Instantiate $ Proxy0 dan Pass Dynamicsubject di dalam konstruktor, lalu $ Proxy0 memanggil konstruktor proxy kelas induk dan memberikan nilai ke h, sebagai berikut:
kelas proxy {InvocationHandler h = null; proxy yang dilindungi (InvocationHandler h) {this.h = h; } ...}Mari kita lihat kode sumber yang mewarisi proxy $ proxy0:
Public Final Class $ Proxy0 memperluas proxy mengimplementasikan subjek {Metode statis pribadi M1; Metode Statis Pribadi M0; Metode statis pribadi M3; metode statis pribadi m2; static {try {m1 = class.forname ("java.lang.object"). getMethod ("equals", class baru [] {class.forname ("java.lang.object")}); m0 = class.forname ("java.lang.object"). getMethod ("hashCode", kelas baru [0]); m3 = class.forname ("***. RealSubject"). getMethod ("request", kelas baru [0]); m2 = class.forname ("java.lang.object"). getMethod ("tostring", kelas baru [0]); } catch (nosuchmethodeException nosuchmethodeException) {lempar nosuchmethoderror baru (nosuchmethodeException.getMessage ()); } catch (ClassNotFoundException ClassNotFoundException) {Throw NoClassDeffoundError baru (ClassNotFoundException.getMessage ()); }} // public static $ proxy0 (InvocationHandler InvocationHandler) {super (InvocationHandler); } @Override public final boolean sama (objek obj) {coba {return ((boolean) super.h.invoke (ini, m1, objek baru [] {obj})) .booleanValue (); } catch (Throwable Throwable) {Throw New UndeclaredthrowableException (Throwable); }} @Override public final int hashCode () {coba {return ((integer) super.h.invoke (ini, m0, null)). Intvalue (); } catch (Throwable Throwable) {Throw New UndeclaredthrowableException (Throwable); }} public final void request () {coba {super.h.invoke (ini, m3, null); kembali; } catch (error e) {} catch (Throwable Throwable) {Throw New UndeclaredThrowableException (Throwable); }} @Override Public Final String ToString () {coba {return (string) super.h.invoke (this, m2, null); } catch (Throwable Throwable) {Throw New UndeclaredthrowableException (Throwable); }}}Kemudian masukkan instance $ proxy0 yang dihasilkan ke dalam subjek dan tetapkan referensi ke subjek. Ketika metode subjek.Request () dieksekusi, metode permintaan () dalam kelas $ proxy0 dipanggil, dan metode hevoke () dari h di proxy kelas induk dipanggil. Yaitu, InvocationHandler.invoke ().
PS: 1. Satu hal yang perlu diperhatikan adalah bahwa metode GetProxyClass di kelas proxy mengembalikan kelas proxy kelas. Alasan untuk ini adalah karena saya membuat kesalahan tingkat rendah di awal, berpikir bahwa pengembalian adalah "kelas kelas kelas proxy"-! Dianjurkan untuk melihat kode sumber GetProxyClass, yang sangat panjang =. =
2. Dari kode sumber $ proxy0, dapat dilihat bahwa kelas proxy dinamis tidak hanya proxy metode dalam antarmuka yang ditentukan, tetapi juga proxy yang diwariskan tiga metode setara (), hashcode (), dan tostring () dalam objek kelas root Java, dan hanya tiga metode.
T: Sejauh ini, masih ada pertanyaan. Parameter pertama dalam metode Invoke adalah instance proxy (tepatnya, contoh $ proxy0 akhirnya digunakan), tetapi apa gunanya? Atau, bagaimana fungsi tersebut muncul dalam program?
A: Dari level saya saat ini, parameter proxy ini tidak berpengaruh. Dalam seluruh mekanisme proxy dinamis, parameter proxy dari metode Invoke di InvocationHandler tidak digunakan. Parameter yang dilewati sebenarnya adalah contoh dari kelas proxy. Saya pikir mungkin untuk membiarkan programmer menggunakan refleksi dalam metode Invoke untuk mendapatkan beberapa informasi tentang kelas proxy.
Meringkaskan
Di atas adalah seluruh konten artikel ini tentang metode Invoke () di InvocationHandler. Saya harap ini akan membantu semua orang. Teman yang tertarik dapat terus merujuk ke situs ini:
Penjelasan terperinci tentang proxy statis musim semi dan kode proxy dinamis
Contoh Metode Injeksi Ketergantungan Kerangka Musim Semi
Implementasi Pemrograman Java Contoh Login Sederhana SpringMVC
Jika ada kekurangan, silakan tinggalkan pesan untuk menunjukkannya.