Proses pemuatan kelas
Pekerjaan utama class loader adalah memuat file kelas ke JVM. Seperti yang ditunjukkan pada gambar di bawah ini, proses dibagi menjadi tiga langkah:
1. Memuat: Temukan file kelas yang akan dimuat dan memuat aliran byte ke JVM;
2. Tautan: Alokasikan struktur memori paling dasar untuk memuat untuk menyimpan informasinya, seperti properti, metode dan kelas yang direferensikan. Pada tahap ini, kelas masih belum tersedia;
(1) Verifikasi: Verifikasi aliran byte yang dimuat, seperti format dan keamanan;
(2) Alokasi Memori: Mempersiapkan ruang memori untuk kelas ini untuk mewakili sifat, metode, dan kelas yang direferensikan;
(3) Analisis: Muat kelas lain yang dirujuk oleh kelas ini, seperti kelas induk, antarmuka yang diimplementasikan, dll.
3. Inisialisasi: Tetapkan nilai ke variabel kelas.
Level loader kelas
Garis putus -putus pada gambar di bawah ini adalah beberapa loader kelas penting yang disediakan oleh JDK, dan deskripsi terperinci adalah sebagai berikut:
(1) Bootstrap Class Loader: Saat memulai kelas yang berisi fungsi utama, muat paket JAR di direktori java_home/lib atau -xbootclasspath direktori yang ditentukan;
(2) Loader kelas ekstensi: Muat paket jar dalam direktori java_home/lib/ext atau -djava.ext.dirs Direktori yang ditentukan.
(3) Loader Kelas Sistem: Muat Paket ClassPath atau -Djava.Class.Path atau Paket JAR di direktori yang ditentukan.
Yang perlu Anda ketahui adalah:
1. Selain bootstrap class loader, loader kelas lainnya adalah subclass dari kelas java.lang.classloader;
2. Bootstrap Class Loader tidak diimplementasikan di Java. Jika Anda tidak menggunakan loader kelas yang dipersonalisasi, maka java.lang.string.class.getClassLoader () adalah nol, dan loader induk dari class loader ekstensi juga nol;
3. Beberapa cara untuk mendapatkan loader kelas:
(1) Mendapatkan Bootstrap Class Loader: Apa yang Anda dapatkan harus nol saat mencoba mendapatkan bootstrap class loader. Anda dapat memverifikasi dengan cara berikut: Gunakan metode GetClassLoader objek kelas dalam paket RT.Jar, seperti java.lang.string.class.getClassLoader () untuk mendapatkan atau mendapatkan loader kelas ekstensi, dan kemudian hubungi metode getParent untuk mendapatkan;
(2) Dapatkan loader kelas ekstensi: Gunakan metode GetClassLoader dari objek kelas dalam paket JAR di bawah direktori java_home/lib/ext atau pertama -tama dapatkan pemuat kelas sistem, dan kemudian dapatkan melalui metode GetParent;
(3) Dapatkan Loader Kelas Sistem: Panggil metode GetClassLoader dari objek kelas yang berisi fungsi utama, atau call thread.currentThread (). GetContextClassLoader () atau call classloader.getSystemClassLoader () dalam fungsi utama;
(4) dapatkan loader kelas yang ditentukan pengguna: hubungi metode getClassLoader dari objek kelas atau call thread.currentThread (). GetContextClassLoader ();
Prinsip Operasional Loader Kelas
1. Prinsip Agen
2. Prinsip visibilitas
3. Prinsip keunikan
4. Prinsip Proksi Prinsip proxy mengacu pada loader kelas yang meminta proxy loader induknya untuk memuat saat memuat kelas, dan loader induk juga akan meminta proxy loader induknya untuk memuat, seperti yang ditunjukkan pada gambar di bawah ini.
Mengapa Menggunakan Mode Proxy? Pertama -tama, ini dapat mengurangi pemuatan duplikat kelas. (Apakah ada alasan lain?)
Di mana salah paham:
Secara umum, diperkirakan bahwa urutan proxy class loader adalah orang tua terlebih dahulu, yaitu:
1. Saat memuat kelas, loader kelas pertama -tama memeriksa apakah telah memuat kelas. Jika telah dimuat, itu akan kembali; Kalau tidak, minta orang tua loader ke proxy;
2. Loader induk mengulangi operasi 1 sampai bootstrap class loader;
3. Jika loader kelas bootstrap tidak memuat kelas, itu akan mencoba memuatnya, dan jika berhasil, itu akan kembali; Jika gagal, ClassNotFoundException dilemparkan, itu akan dimuat oleh pemuat anak;
4. Loader subkelas menangkap pengecualian dan mencoba memuatnya. Jika berhasil, ia kembali. Jika gagal, ia melempar ClassNotFoundException hingga Loaded Subclass Loader dimulai.
Pemahaman ini benar untuk loader seperti bootstrap class loader, loader kelas ekstensi, dan loader kelas sistem, tetapi beberapa loader yang dipersonalisasi tidak terjadi. Misalnya, beberapa loader kelas yang diimplementasikan oleh IBM Web Sphere Portal Server adalah Parent Last, yang merupakan loader anak yang mencoba memuat terlebih dahulu. Jika beban gagal, loader induk akan ditanyakan. Alasannya adalah: jika Anda mengharapkan versi Log4j tertentu untuk digunakan oleh semua aplikasi, letakkan di perpustakaan Was_Home, dan itu akan dimuat saat dimulai. Jika suatu aplikasi ingin menggunakan versi lain dari log4j, itu tidak dapat dicapai jika menggunakan orang tua terlebih dahulu karena kelas di log4j sudah dimuat di loader induk. Namun, jika orang tua terakhir digunakan, loader kelas yang bertanggung jawab untuk memuat aplikasi akan memprioritaskan memuat versi LOG4J lain.
Prinsip Visibilitas
Visibilitas setiap kelas ke loader kelas berbeda, seperti yang ditunjukkan pada gambar di bawah ini.
Untuk memperluas pengetahuan, OSGI memanfaatkan fitur ini. Setiap bundel dimuat oleh loader kelas terpisah, sehingga setiap loader kelas dapat memuat versi kelas tertentu, sehingga seluruh sistem dapat menggunakan beberapa versi kelas.
Prinsip keunikan
Setiap kelas dimuat paling banyak sekali di loader.
Knowledge Extended 1: Tepatnya, pola singleton mengacu pada objek singleton yang hanya satu salinan kelas dalam satu set class loader.
Extended Knowledge 2: Kelas dapat dimuat oleh beberapa loader kelas. Setiap objek kelas ada di namespace sendiri. Saat membandingkan objek kelas atau tipe yang mengonversi instance, itu akan membandingkan namespace masing -masing pada saat yang sama, seperti:
Kelas Klass dimuat oleh ClassLoadera, dengan asumsi objek kelas adalah Klassa; Ini dimuat oleh ClassLoadERB, dengan asumsi objek kelas adalah Klassb, maka Klassa tidak sama dengan Klassb. Pada saat yang sama, ketika instance dari ClassA dilemparkan ke Klassb, pengecualian ClassCastException akan dilemparkan.
Mengapa Anda membutuhkan loader kelas yang dipersonalisasi? Loader kelas yang dipersonalisasi menambah banyak fleksibilitas pada bahasa Java. Penggunaan utama adalah:
1. Kelas dapat dimuat dari beberapa tempat, seperti di jaringan, dalam database, atau bahkan secara instan menyusun file sumber;
2. Setelah personalisasi, loader kelas dapat memuat versi tertentu dari file kelas pada prinsipnya saat runtime;
3. Setelah personalisasi, loader kelas dapat secara dinamis menurunkan beberapa kelas;
4. Setelah personalisasi, loader kelas dapat mendekripsi dan mendekompresi kelas sebelum memuat kelas.
Pemuatan kelas implisit dan eksplisit
Pemuatan implisit: Ketika suatu kelas dirujuk, diwariskan atau dipakai, akan dimuat secara implisit. Jika beban gagal, noclassdeffounderror dilemparkan.
Pemuatan Eksplisit: Gunakan metode berikut, jika beban gagal, ClassNotFoundException akan dilemparkan.
Cl.LoadClass (), CL adalah instance dari class loader;
Class.forname (), memuat menggunakan class loader dari kelas saat ini.
Jika eksekusi blok statis kelas memiliki kelas berikut:
paket cn.fengd; dummy kelas publik {static {system.out.println ("hai"); }} Buat kelas tes lain:
paket cn.fengd; Public Class ClassLoArtest {public static void main (String [] args) melempar InstantiationException, Exception {try { / * * berbagai cara memuat. */ Class c = classLoaderTest.class.getClassLoader (). LoadClass ("cn.fengd.dummy"); } catch (Exception e) {// TODO Auto-Entoerated Catch Block E.PrintStackTrace (); }}}Seberapa efektif setelah berlari?
Bagaimana jika digantikan oleh class.forname ("cn.fengd.dummy"); atau boneka baru ()?
Hai akan menjadi output.
Pertanyaan yang Sering Diajukan:
1. Apakah tipe yang ditentukan dimuat oleh loader kelas yang berbeda masih jenis yang sama?
Di Java, kelas menggunakan nama kelas yang sepenuhnya memenuhi syarat sebagai pengidentifikasi. Nama kelas pertandingan yang tepat yang dimaksud di sini termasuk nama paket dan nama kelas. Namun, dalam JVM, kelas menggunakan nama lengkapnya dan instance dari ClassLoader kelas pemuatan sebagai pengidentifikasi unik. Kelas -kelas yang dimuat oleh loader kelas yang berbeda akan ditempatkan di ruang nama yang berbeda. Kami dapat menggunakan dua loader kelas khusus untuk memuat tipe khusus (Catatan yang tidak menempatkan bytecode jenis kustom ke dalam jalur sistem atau jalur ekstensi, jika tidak akan dimuat terlebih dahulu oleh loader kelas sistem atau loader kelas ekstensi), dan kemudian menggunakan dua instance kelas yang diperoleh untuk membuat java.Lang.Object.Equals (...) Judgments, dan Anda akan mendapatkan hasil yang lebih besar. Ini dapat dilakukan dengan menulis dua loader kelas khusus untuk memuat jenis kustom yang sama dan kemudian membuat penilaian; Pada saat yang sama, Anda dapat menguji pemuatan Java.* Jenis, dan kemudian membandingkan dan menguji hasil tes.
2. Langsung hubungi metode class.forname (nama string) dalam kode. Loader kelas mana yang akan memicu perilaku pemuatan kelas?
Class.forname (nama string) akan menggunakan class loader yang memanggil kelas untuk memuat kelas secara default. Mari kita analisis kode JDK yang sesuai secara langsung:
//java.lang.class.java PublicStatic Class <?> FORNAME (String ClassName) melempar ClassNotFoundException {return forname0 (classname, true, classloader.getcallerclassloader ());} // java. getCallerClassLoader () {// Dapatkan jenis kelas panggilan (penelepon) class caller = reflection.getCallerClass (3); // Ini bisa null jika VM memintanya jika (penelepon == null) {returnNull; } // Panggil metode lokal di java.lang.class untuk mendapatkan classloader yang memuat kelas panggilan (penelepon) caller.getClassLoader0 ();} // java.lang.class.java//native dari mesin virtual untuk mendapatkan class loader dari class loaderer classLoader ClassLoader saat ini () dari kelas saat ini, Get ClassLoader ClassLoader ClassLoader saat ini () dari ClassLoader ClassLoader ClassLoader saat ini () dari ClassLoader ClassLoader ClassLoader saat ini () dari ClassLoader ClassLoader CLASSLOADER ADARN 3. Saat menulis loader kelas khusus, jika orang tua loader tidak diatur, lalu apa loader induknya?
Ketika loader kelas induk tidak ditentukan, loader kelas sistem digunakan secara default. Beberapa orang mungkin tidak mengerti, sekarang mari kita lihat implementasi kode JDK yang sesuai. Seperti yang kita semua tahu, kita menulis loader kelas kustom secara langsung atau tidak langsung mewarisi dari kelas abstrak java.lang.classloader. Konstruktor default yang sesuai tanpa parameter diimplementasikan sebagai berikut:
// kutipan dari java.lang.classloader.javaprotected classloader () {SecurityManager Security = System.GetSecurityManager (); if (Security! = null) {Security.CheckCreateClassLoader (); } this.parent = getSystemClassLoader (); diinisialisasi = true;}Mari kita lihat implementasi yang sesuai dari metode GetSystemClassLoader ():
privatestaticsynchronized initsystemclassloader () {// ... sun.misc.launcher l = sun.misc.launcher.getLauncher (); scl = l.getClassLoader (); // ...}Kita dapat menulis kode tes sederhana untuk mengujinya:
System.out.println (sun.misc.launcher.getLauncher (). GetClassLoader ());
Output yang sesuai dari mesin ini adalah sebagai berikut:
sun.misc.launcher$Appclassloader@197d257
Jadi, kita sekarang dapat percaya bahwa ketika loader kelas kustom tidak menentukan loader kelas induk, loader kelas induk default adalah pemuat kelas sistem. Pada saat yang sama, kita dapat menarik kesimpulan berikut:
Loader kelas yang ditentukan pengguna instan tidak menentukan loader kelas induk, maka kelas di tiga tempat berikut juga dapat dimuat:
(1) Kelas di bawah <Java_runtime_home>/lib
(2) Kelas di lokasi yang ditentukan oleh variabel sistem java.ext.dir
(3) Kelas di bawah jalur kelas proyek saat ini atau di lokasi yang ditentukan oleh variabel sistem java.class.path
4. Saat menulis loader kelas khusus, apa yang akan terjadi jika loader kelas induk dipaksa menjadi nol? Jika loader kelas kustom tidak dapat memuat kelas yang ditentukan, apakah itu pasti gagal?
Spesifikasi JVM menetapkan bahwa jika loader kelas yang ditentukan pengguna memaksa loader kelas induk ke nol, pemuat kelas startup akan secara otomatis diatur ke loader kelas induk dari loader kelas yang ditentukan pengguna saat ini (masalah ini telah dianalisis sebelumnya). Pada saat yang sama, kita dapat menarik kesimpulan berikut:
Jika loader kelas yang ditentukan pengguna instan tidak menentukan loader kelas induk, maka ia juga dapat memuat kelas di bawah <java_runtime_home>/lib, tetapi kelas di bawah direktori <java_runtime_home>/lib/lib tidak dapat dimuat saat ini.
Catatan: Kesimpulan yang disimpulkan dari pertanyaan 3 dan 4 didasarkan pada loader kelas yang ditentukan pengguna itu sendiri yang melanjutkan logika delegasi default dari java.lang.classloader.loadClass (...). Jika pengguna mengubah logika delegasi default ini, kesimpulan yang disimpulkan di atas mungkin tidak valid. Lihat pertanyaan 5 untuk detailnya.
5. Apa poin perhatian umum saat menulis loader kelas khusus?
(1) Secara umum, cobalah untuk tidak mengganti logika delegasi dalam metode LoadClass (...) yang ada. Secara umum, itu dilakukan dalam versi sebelum JDK 1.2, dan ternyata melakukan hal itu sangat mungkin menyebabkan loader kelas default sistem tidak berfungsi dengan baik. Dalam spesifikasi JVM dan dokumentasi JDK (1.2 atau lebih lambat), tidak disarankan agar pengguna menimpa metode LoadClass (...). Sebaliknya, pengembang jelas diingatkan untuk menimpa logika FindClass (...) saat mengembangkan loader kelas khusus. Ambil contoh untuk memverifikasi masalah:
// Pengguna Custom Class Loader WrongClassLoader.java (Overwrite LoadClass Logic) PublicClassWrongClassEkextends ClassLoader {Public Class <?> LoadClass (Name String) melempar ClassNotFoundException {returnis.findclass (name); } kelas yang dilindungi <?> findClass (nama string) melempar ClassNotFoundException {// Misalkan di sini hanya untuk direktori tertentu selain dari proyek D: /pustaka untuk memuat kode implementasi spesifik kelas yang dihilangkan}}Melalui analisis sebelumnya, kita sudah tahu bahwa default loader kelas yang ditentukan pengguna (WrongClassLoader)
Loader kelas yang diakui adalah pemuat kelas sistem, tetapi kesimpulan dari empat jenis masalah tidak valid sekarang. Semua orang bisa
Cukup uji, sekarang <Java_runtime_home>/lib, <Java_runtime_home>/lib/ext dan
Kelas di jalur kelas program tidak dapat dimuat.
Pertanyaan 5 Kode Tes 1
PublicClass WrongClassLoadEstest {publicstaticvoid main (string [] args) {coba {wrongClassLoader loader = new WrongClassLoader (); Class classloaded = loader.loadClass ("beans.account"); System.out.println (classloaded.getName ()); System.out.println (ClassLoaded.getClassLoader ()); } catch (Exception e) {E.PrintStackTrace (); }}}(Catatan: D: "Kelas" Kacang "Akun.class secara fisik ada)
Hasil output:
java.io.filenotfoundException: D: "Kelas" Java "Lang" Object.class (Sistem tidak dapat menemukan jalur yang ditentukan.) Di java.io.fileinputStream.open (metode asli) di java.io.fileinputstream. <inin> (FileinputStream.java:106) AttileInputStream. WrongClassLoader.findClass (WrongClassLoader.java:40) di WrongClassLoader.LoadClass (WrongClassLoader.java:29) di java.lang.classloader.LoadClassinternal (classloader.java) di java.Lang.classloader.dasloaderer.java) di java.Lang.classloader.daskloader.def) java.lang.classloader.defineclass (classloader.java:620) di java.lang.classloader.defineclass (classloader.java:400) di Wrongclasserer.findClass (WALITClasserer.java:43) di Wrongclasserer.findclass (WALITCLASSLOADER.java:43) di WrongClasserer.findclass (WALLASSLASAS.JAVA.JAVA:43) di Wrongclasser. WALITCLASSLOADERTEST.MAIN (WALITCLASSLOADERTEST.JAVA:27) Pengecualian di utas "utama" java.lang.noclassdeffoundError: java/lang/objek di java.Lang.classloader.defineclass1 (metode asli) di java.Lang.lasloader.defineclass1 (metode asli) di java.Lang.lasloader.deref java.lang.classloader.defineclass (classloader.java:400) di WrongClassLoader.findClass (WrongclassLoader.java:43) di WrongclassLoader.main (WrongClassLoader.java:29) di WrongclassEloDerTest.main (WrongClassloader.java:29) di WrongClasseTtest (WRITEDRASSLASRASS.JAVA:29) di WrongClasseTesttest (WrongClassRasserer
Ini berarti bahwa bahkan supertype java.lang.Object dari jenis yang akan dimuat tidak dapat dimuat. Kesalahan logis yang tercantum di sini disebabkan oleh penimpangan LoadClass (...) jelas relatif sederhana, dan kesalahan logis yang disebabkan pada kenyataannya mungkin jauh lebih rumit.
Pertanyaan 5 Tes 2
// Pengguna Custom Class Loader WrongClassLoader.java (Jangan implite LoadClass Logic) publicClassWrongClassLoadExtends ClassLoader {Kelas Protected <?> FindClass (String Name) Melempar ClassNotFoundException { /Setelah memodifikasi kode loader kelas kustom WrongClassLoader.java, jalankan kode uji, dan hasil output adalah sebagai berikut:
beans.accountwrongclassloader@1c78e57
Ini menunjukkan bahwa Beans.Account berhasil dimuat dan dimuat oleh kelas kustom Loader WrongClassLoader.
Saya tidak berpikir ada kebutuhan untuk menjelaskan alasan untuk ini, dan Anda harus dapat menganalisisnya.
(2) Mengatur dengan benar Loader kelas induk melalui analisis pertanyaan 4 dan pertanyaan 5 di atas. Saya pribadi berpikir ini adalah poin terpenting ketika menyesuaikan loader kelas pengguna, tetapi sering diabaikan atau mudah dibawa. Dengan analisis kode JDK sebelumnya sebagai dasar, saya pikir semua orang dapat memberikan contoh apa pun sekarang.
(3) Pastikan kebenaran logis dari metode FindClass (String), cobalah untuk secara akurat memahami tugas -tugas pemuatan yang akan diselesaikan oleh loader kelas untuk ditentukan, dan memastikan bahwa konten bytecode yang sesuai dapat diperoleh sampai tingkat terbesar.
6. Bagaimana menentukan jalur mana yang dapat dimuat oleh loader kelas sistem?
Pertama, Anda dapat secara langsung menghubungi ClassLoader.getSystemClassLoader () atau metode lain untuk mendapatkan loader kelas sistem (loader kelas sistem dan loader kelas ekstensi itu sendiri berasal dari urlclassloader), dan Anda bisa mendapatkannya dengan memanggil metode getUrls () dalam urlclassloader;
Kedua, Anda dapat secara langsung melihat informasi entri pada ClassPath saat ini dengan mendapatkan atribut sistem java.class.path. System.getProperty ("java.class.path")
7. Bagaimana menentukan jalur mana yang dapat dimuat oleh loader kelas ekstensi standar?
Metode Satu:
coba {url [] exturls = ((urlclassloader) classloader.getSystemClassLoader (). getParent ()). getUrls (); untuk (int i = 0; i <exturls.length; i ++) {System.out.println (exturls [i]); }} catch (Exception e) {//…}Output yang sesuai dari mesin ini adalah sebagai berikut:
File:/d: /demo/jdk1.5.0_09/jre/lib/ext/dnsns.jarfile:/d: /demo/jdk1.5.0_09/jre/lib/ext/localedata.jarfile :/D: /demo/jdk1.5.0_09/jre/lib/ext/sunjce_provider.jarfile:/d: /demo/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar