Model delegasi orang tua
Konsep pemuatan kelas harus dianggap sebagai inovasi dalam bahasa Java. Tujuannya adalah untuk memisahkan proses pemuatan kelas dari mesin virtual dan mencapai tujuan "mendapatkan aliran byte biner yang menggambarkan kelas ini melalui nama kelas yang memenuhi syarat." Modul kode yang mengimplementasikan fungsi ini adalah class loader. Model dasar loader kelas adalah model delegasi orang tua yang terkenal. Kedengarannya luar biasa, tetapi logikanya sebenarnya sangat sederhana. Ketika kita perlu memuat kelas, pertama -tama kita menentukan apakah kelas telah dimuat. Jika tidak, kami menentukan apakah telah dimuat oleh loader induk. Jika kita belum memanggil metode FindClass kita sendiri untuk mencoba memuat. Ini adalah model dasar (Hapus Pelanggaran Gambar Pencurian):
Ini juga sangat mudah diimplementasikan. Kuncinya adalah metode LoadClass dari kelas ClassLoader. Kode sumber adalah sebagai berikut:
kelas yang dilindungi <?> LoadClass (nama string, boolean resolve) melempar ClassNotFoundException {disinkronkan (getClassLoadingLock (name)) {// pertama, periksa apakah kelas sudah dimuat kelas <?> C = findloadedClass (name); if (c == null) {long t0 = system.nanoTime (); Coba {if (parent! = null) {c = parent.LoadClass (name, false); } else {c = findbootstrapclassornull (name); }} catch (ClassNotFoundException e) {// ClassNotFoundException dilemparkan jika kelas tidak ditemukan // dari class non-null class loader} if (c == null) {// Jika masih belum ditemukan, maka buka FindClass secara berurutan // untuk menemukan kelas. long t1 = system.nanoTime (); c = findClass (name); // Ini adalah loader kelas yang menentukan; Catat statistik Sun.misc.perfcounter.getParentDelegationTime (). AddTime (T1 - T0); sun.misc.perfcounter.getFindClasTime (). AddelapsedTimefrom (T1); sun.misc.perfcounter.getFindClasses (). increment (); }} if (resolve) {class (c); } return c; }}Tiba -tiba saya merasa digoda, mengapa saya hanya melempar pengecualian secara langsung? Bahkan, itu karena kelas ClassLoader adalah kelas abstrak. Bahkan, itu akan menulis subclass saat digunakan. Metode ini akan ditulis ulang sesuai kebutuhan untuk menyelesaikan proses pemuatan yang diperlukan oleh bisnis.
Classloader khusus
Saat menyesuaikan subclass dari ClassLoader, ada dua metode umum: satu adalah untuk menulis ulang metode LoadClass, dan yang lainnya adalah menulis ulang metode FindClass. Bahkan, kedua metode ini pada dasarnya sama. Lagi pula, LoadClass juga akan memanggil FindClass, tetapi secara logis, yang terbaik adalah tidak secara langsung memodifikasi logika internal LoadClass.
Saya pribadi berpikir cara yang lebih baik adalah dengan hanya menulis ulang metode pemuatan kelas kustom di FindClass.
Mengapa ini lebih baik? Karena saya juga mengatakan sebelumnya bahwa metode LoadClass adalah tempat untuk mengimplementasikan logika model orangtua-delegasi. Memodifikasi metode ini tanpa otorisasi akan menyebabkan model dihancurkan dan dengan mudah menyebabkan masalah. Oleh karena itu, yang terbaik adalah membuat perubahan skala kecil dalam kerangka model delegasi induk untuk tidak menghancurkan struktur stabil asli. Pada saat yang sama, ia juga menghindari kebutuhan untuk menulis kode duplikat yang didelegasikan oleh orang tua dalam proses menulis ulang metode LoadClass. Dari perspektif penggunaan kembali kode, tidak memodifikasi metode ini secara langsung selalu merupakan pilihan yang lebih baik.
Tentu saja, akan berbeda jika Anda sengaja menghancurkan model Komisi Orang Tua.
Hancurkan Model Delegasi Orang Tua
Mengapa menghancurkan model delegasi induk?
Bahkan, dalam beberapa kasus, kita mungkin perlu memuat dua kelas yang berbeda, tetapi sayangnya, nama -nama kedua kelas ini persis sama. Pada saat ini, model delegasi induk tidak dapat memenuhi persyaratan kami. Kita perlu menulis ulang metode LoadClass untuk menghancurkan model delegasi induk dan membiarkan nama kelas yang sama memuat beberapa kali. Tentu saja, kehancuran yang disebutkan di sini hanya kehancuran dalam pengertian lokal.
Tetapi nama kelasnya sama, bagaimana JVM bisa membedakan kedua kelas ini? Jelas, ini tidak akan menyebabkan keruntuhan pandangan dunia. Faktanya, kelas tidak hanya dibatasi oleh nama kelas di JVM, tetapi juga milik ClassLoader yang memuatnya. Kelas yang dimuat oleh classloader yang berbeda sebenarnya tidak saling mempengaruhi.
Lakukan percobaan.
Mari Tulis Dua Kelas Dulu:
Paket com.mythsman.test; kelas publik Hello {public void say () {System.out.println ("Ini dari Hello v1"); }} Paket com.mythsman.test; kelas publik Hello {public void say () {System.out.println ("Ini dari Hello v2"); }} Dua nama kelas adalah sama, satu -satunya perbedaan adalah bahwa implementasi metode berbeda. Kami pertama -tama mengkompilasi secara terpisah, dan kemudian mengganti nama file kelas yang dihasilkan menjadi hello.class.1 dan hello.class.2.
Tujuan kami adalah membuat contoh dari dua kelas ini di kelas tes.
Kemudian kami membuat kelas tes baru com.mythsman.test.main dan membuat dua classloader kustom di fungsi utama:
Classloader classloader1 = classloader baru () {@Override Public Class <?> LoadClass (String S) melempar ClassNotFoundException {coba {if (s.equals ("com.mythsman.test.hello")) {byte [] classbytes = File.readallbytes (paths.get ("/home/mitos/desktop/test/hello.class.1")); return defineclass (s, classbytes, 0, classbytes.length); } else {return super.loadClass (s); }} catch (ioException e) {lempar classnotfoundException baru baru; }}}; ClassLoader classloader2 = classloader baru () {@Override Public Class <?> LoadClass (String S) melempar ClassNotFoundException {coba {if (s.Equals ("com.mythsman.test.hello")) {byte [] classbytes = File.readallbytes (paths.get ("/home/mitos/desktop/test/hello.class.2")); return defineclass (s, classbytes, 0, classbytes.length); } else {return super.loadClass (s); }} catch (ioException e) {lempar classnotfoundException baru baru; }}};
Tujuan dari dua classloader ini adalah untuk mengaitkan dua bytecode berbeda dari kelas Hello secara terpisah. Kita perlu membaca file bytecode dan memuatnya ke kelas melalui metode Defineclass. Perhatikan bahwa kami membebani metode LoadClass. Jika kita membebani metode FindClass, maka karena mekanisme pemrosesan delegasi induk dari metode LoadClass, metode FindClass dari classloader kedua tidak akan dipanggil.
Jadi bagaimana kita menghasilkan instance? Jelas, kami tidak dapat secara langsung merujuk ke nama kelas (konflik nama), jadi kami hanya dapat menggunakan refleksi:
Objek hellov1 = classloader1.loadClass ("com.mythsman.test.hello"). NewInstance (); objek hellov2 = classloader2.loadClass ("com.mythsman.test.hello"). newinstance (); hellov1.getClass (). getMethod ("katakan"). Aible (hellov1); hellov2.getclass (). getMethod ("katakan"). Affoke (hellov2); Keluaran:
Ini dari Hello V1 ini dari Hello v2
Oke, bahkan jika Anda telah menyelesaikan dua beban, masih ada beberapa poin yang harus diperhatikan.
Apa hubungan antara dua kelas
Jelas, kedua kelas ini bukan kelas yang sama, tetapi nama mereka sama. Jadi apa hasil dari operator seperti isInstance dari:
System.out.println ("Kelas:"+Hellov1.getClass ()); System.out.println ("Kelas:"+Hellov2.getClass ()); System.out.println ("Has hcode: "+hellov2.getClass (). hashcode ()); System.out.println (" ClassLoader: "+hellov1.getClass (). getClassLoader ()); System.out.println (" ClassLoader: "+Hellov2.getClass (). GetClasserer ()); Keluaran:
Kelas: kelas com.mythsman.test.helloclass: class com.mythsman.test.hellohashcode: 1581781576HashCode: 1725154839ClassLoader: com.mythsman.test.main$1@5e2de80cclasserer: com.main$1
Nama -nama kelas mereka memang sama, tetapi kode hash dari kelas berbeda, yang berarti bahwa keduanya pada dasarnya bukan kelas yang sama, dan loader kelas mereka juga berbeda (pada kenyataannya, mereka adalah dua kelas internal utama).
Apa hubungan antara dua loader kelas ini dan loader kelas tiga lapis dari sistem?
Ambil loader kelas kustom pertama sebagai contoh:
System.out.println (classloader1.getParent (). GetParent (). GetParent ()); System.out.println (classloader1.getParent (). GetParent ()); system.out.println (classlo ader1.getParent ()); System.out.println (classloader1.getParent ()); System.out.println (classLoader1); System.out.println (classloader.getSystemClassLoader ());
Keluaran:
nullsun.misc.launcher $ [email protected] $ [email protected] $ [email protected] $ applassloader@18B4AAC2
Tentu saja, hubungan orang tua-anak yang disebutkan di sini bukanlah hubungan warisan, tetapi hubungan kombinasi. Classloader anak menyimpan referensi (induk) dari classloader induk.