latar belakang
Dalam skenario panggilan antarmuka RPC atau menggunakan skenario proxy dinamis, sebuah yang tidak dapat dideklarasikan dapat terjadi sesekali terjadi, atau dalam skenario refleksi, terjadi penargetan InvocationSception, yang tidak konsisten dengan pengecualian yang kami harapkan, dan informasi pengecualian nyata tersembunyi di tumpukan yang lebih dalam. Artikel ini akan fokus pada analisis yang tidak dapat dideklarasi yang dapat dideklarasikan
Berikan kesimpulan terlebih dahulu
Saat menggunakan antarmuka proxy dinamis JDK, jika pengecualian yang terdeteksi dilemparkan selama pelaksanaan metode tetapi tanda tangan metode tidak menyatakan pengecualian, itu akan dibungkus dalam kelas proxy sebagai sebuah yang tidak dapat dideklarasikan yang dapat dideklarasikan.
Kembalikan masalahnya
// Definisi Antarmuka Antarmuka Publik IService {void foo () melempar sqlexception;} Layanan kelas publik Implement IService {@Override public void foo () melempar sqlexception {throw sqlexception baru ("I uji lemparan pengecualian yang diperiksa"); }} // Proxy Dynamic Public Class IserviceProxy mengimplementasikan InvocationHandler {Private Object Target; IserviceProxy (objek target) {this.target = target; } @Override Public Object Invoke (objek proxy, metode metode, objek [] args) melempar lempar {return method.invoke (target, args); }} kelas publik Maintest {public static void main (string [] args) {iservice service = new serviceImpl (); IService serviceProxy = (iservice) proxy.newproxyInstance (service.getClass (). GetClassLoader (), service.getClass (). GetInterfaces (), iserviceProxy baru (service)); coba {serviceProxy.foo (); } catch (Exception e) {E.PrintStackTrace (); }}}Jalankan pemeliharaan di atas dan tumpukan pengecualian
java.lang.reflect.undeclaredthrowableException di com.sun.proxy. $ proxy0.foo (sumber yang tidak diketahui) di com.learn.reflect.maintest.main (coTerest.java:16) disebabkan oleh: java.lang.reflect.InlineSceptEccepteCCEPTECECEKSCEPTECED oleh: java.lang.reflect.InlineSceptEccepteCEccept ATEKCEPEK sun.reflect.nativeMethodaccessorImpl.invoke0 (metode asli) di sun.reflect.nativeMethodaCessorImpl.invoke (nativeMethoDacCessorMpl.java:62) di sun.reflect.delegatingmethodaCessImpl.invoke (delegatingmethlect. java.lang.reflect.method.invoke (Method.java:498) di com.learn.reflect.iserviceproxy.invoke (iserviceProxy.java:19) ... 2 MorecaSeed oleh: java.sql.sqlexception: I uji coba centang di: java.sql.sqlexception: I uji coba cek com.learn.reflect.serviceImpl.foo (serviceImpl.java:11) ... 7 lagi
Apa yang kami harapkan
java.sql.sqlexception: Saya menguji lemparan pengecualian yang diperiksa di com.learn.reflect.serviceImpl.foo (serviceImpl.java:11) ...
Penyebab Analisis
Dalam pemulihan masalah di atas, SQLException yang sebenarnya dibungkus dalam dua lapisan, pertama kali dibungkus oleh InvocationTargetException, dan kemudian dibungkus oleh Undeclaredthrowable Exception. Di antara mereka, InvocationTargetException adalah pengecualian yang terdeteksi, dan InclaredthrowableException adalah pengecualian runtime. Mengapa dibungkus? Ini juga dimulai dengan kelas proxy yang dihasilkan oleh proxy dinamis.
Proxy dinamis JDK akan menghasilkan kelas implementasi spesifik dari antarmuka delegasi saat runtime. Kami secara manual menghasilkan file kelas melalui proxygenerator, dan kemudian menggunakan ide untuk menguraikan file kelas untuk mendapatkan kelas proxy spesifik: mencegat bagian:
kelas akhir public iserviceProxy $ 1 memperluas proxy mengimplementasikan IService {private static Method M1; metode statis pribadi m2; Metode statis pribadi M3; Metode Statis Pribadi M0; IServiceProxy Publik $ 1 (InvocationHandler var1) melempar {super (var1); } public final void foo () melempar sqlexception {coba {super.h.invoke (this, m3, (objek []) null); } catch (runtimeException | sqlexception | error var2) {throw var2; } catch (throwable var3) {lempar baru undeclaredthrowableException (var3); }} static {try {m1 = class.forname ("java.lang.object"). getMethod ("sama", kelas baru [] {class.forname ("java.lang.object")}); m2 = class.forname ("java.lang.object"). getMethod ("tostring", kelas baru [0]); m3 = class.forname ("com.learn.reflect.iservice"). getMethod ("foo", kelas baru [0]); m0 = class.forname ("java.lang.object"). getMethod ("hashCode", kelas baru [0]); } catch (nosuchmethodException var2) {lempar nosuchmethoderror baru (var2.getMessage ()); } catch (classNotFoundException var3) {throw new noclassDeffoundError (var3.getMessage ()); }}}Saat memanggil metode FOO dari "Kelas Delegasi", metode Foo dari kelas proxy iserviceProxy $ 1 sebenarnya disebut, dan logika utama kelas proxy adalah dengan memanggil metode Invoke dari AvocationHandler. Logika penanganan pengecualian adalah untuk secara langsung melempar runtimeException, pengecualian yang dinyatakan oleh antarmuka, dan kesalahan dilemparkan, dan pengecualian lainnya dibungkus sebagai tidak dideklarasikan yang dapat dideklarasikan. Pada titik ini, mungkin Anda sudah mendapatkannya, mungkin Anda memiliki pertanyaan, dalam implementasi antarmuka, memang melempar sqlexception baru, mengapa masih dibungkus? Mari kita lihat metode Invoke IserviceProxy. Ini secara langsung mengeksekusi metode target melalui refleksi. Inilah masalahnya. Method.invoke (objek OBJ, objek ... args) Metode Deklarasi telah dijelaskan bahwa jika metode target melempar pengecualian, itu akan dibungkus sebagai InvocationTargetException. (Untuk detailnya, silakan periksa Javadoc)
Oleh karena itu, ringkasannya adalah: dalam implementasi metode spesifik, SQLException dilemparkan dan tercermin dan dibungkus sebagai InvocationTargetException. Ini adalah pengecualian yang diperiksa. Ketika kelas proxy menangani pengecualian, ia menemukan bahwa pengecualian tidak dinyatakan dalam antarmuka, sehingga dikemas sebagai yang tidak dapat dideklarasikan.
Larutan
Dalam Badan Metode Invoke yang mengimplementasikan InvocationHandler, coba tangkap metode.invoke (target, args); Hubungi, dan lemparkan penyebab InvocationTargetException. Sekarang:
@Override Public Object Invoke (objek proxy, metode metode, objek [] args) melempar lempar {coba {return method.invoke (target, args); } catch (InvocateTargetException e) {throw e.getCause (); }}Di luar topik
Mengapa pengecualian memeriksa yang tidak dideklarasikan di kelas proxy berubah menjadi Exception yang tidak dideklarasikan? Karena prinsip warisan Java: Ketika subkelas menimpa kelas induk atau metode yang mengimplementasikan antarmuka induk, pengecualian yang dilemparkan harus berada dalam daftar pengecualian yang didukung oleh metode asli. Kelas proxy mengimplementasikan antarmuka induk atau menimpa metode kelas induk
merujuk ke
https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html#icomments
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.