Saat melakukan pengembangan Java, pengetahuan dasar yang harus Anda kenal dalam mekanisme ClassLoader. Artikel ini secara singkat merangkum mekanisme Java Classloader. Karena implementasi JVM yang berbeda berbeda, konten yang dijelaskan dalam artikel ini terbatas pada Hotspot JVM.
Artikel ini akan dimulai dengan empat aspek ClassLoader, model delegasi orang tua yang disediakan oleh JDK, cara menyesuaikan classloader dan skenario yang merusak mekanisme delegasi induk di Java.
JDK Default ClassLoader
JDK menyediakan classloader berikut secara default
Bootstrp loader
Bootstrp Loader ditulis dalam bahasa C ++. Ini diinisialisasi setelah mesin virtual Java dimulai. Ini terutama bertanggung jawab untuk memuat jalur yang ditentukan oleh %java_home %/jre/lib, -xbootclasspath parameter dan kelas dalam %java_home %/jre/kelas.
Extclassloader
Bootstrp Loader memuat extclassLoader, dan menetapkan loader induk dari extclassloader ke bootstrp loader.ExtClassLoader ditulis di Java, khususnya, sun.misc.launcher $ extclassloader. ExtclassLoader terutama memuat %java_home %/jre/lib/ext, semua direktori kelas di bawah jalur ini dan pustaka kelas di jalur yang ditentukan oleh variabel sistem java.ext.dirs.
AppClassLoader
Setelah bootstrp loader memuat ExtClassLoader, AppClassLoader akan dimuat, dan loader induk dari AppClassLoader ditentukan sebagai ExtClassLoader. AppClassloader juga ditulis di Java. Kelas implementasinya adalah Sun.misc.launcher $ appclassloader. Selain itu, kita tahu bahwa ada metode GetSystemClassloader di ClassLoader. Metode ini mengembalikan appclassloader.AppClassLoader terutama bertanggung jawab untuk memuat dokumen kelas atau JAR di lokasi yang ditentukan oleh ClassPath. Ini juga merupakan loader kelas default untuk program Java.
Model delegasi orang tua
Pemuatan ClassLoader di Java mengadopsi mekanisme delegasi orang tua. Saat memuat kelas menggunakan mekanisme delegasi orang tua, langkah -langkah berikut diadopsi:
Saat ini, ClassLoader pertama -tama memeriksa apakah kelas ini telah dimuat dari kelas yang telah dimuat. Jika telah dimuat, itu akan secara langsung mengembalikan kelas asli.
Setiap loader kelas memiliki cache pemuatan sendiri. Ketika kelas dimuat, itu akan dimasukkan ke dalam cache dan dapat dikembalikan secara langsung ketika dimuat di lain waktu.
Ketika cache ClassLoader tidak ditemukan, loader kelas induk didelegasikan untuk dimuat. Loader kelas induk mengadopsi strategi yang sama. Pertama, periksa cache sendiri, dan kemudian mendelegasikan kelas induk dari kelas induk untuk dimuat, sampai ke bootstrp classloader.
Ketika semua loader kelas induk tidak dimuat, mereka dimuat oleh loader kelas saat ini dan memasukkannya ke dalam cache sendiri sehingga mereka dapat dikembalikan secara langsung saat berikutnya ada permintaan pemuatan.
Berbicara tentang ini, Anda mungkin bertanya -tanya, mengapa Java mengadopsi mekanisme delegasi seperti itu? Untuk memahami masalah ini, kami memperkenalkan konsep lain "namespace" tentang ClassLoader, yang berarti bahwa untuk menentukan kelas tertentu, Anda memerlukan nama kelas yang memenuhi syarat dan memuat kelas ini ClassLoader untuk secara bersama -sama menentukannya. Dengan kata lain, bahkan jika nama -nama yang sepenuhnya memenuhi syarat dari kedua kelas tersebut sama, karena classloader yang berbeda memuat kelas ini, maka itu adalah kelas yang berbeda dalam JVM. Setelah memahami namespace, mari kita lihat model delegasi. Setelah mengadopsi model delegasi, kemampuan interaktif dari berbagai classloader meningkat. Misalnya, seperti yang disebutkan di atas, pustaka kelas yang disediakan oleh JDK Binsheng kami, seperti HashMap, LinkedList, dll. Setelah kelas -kelas ini dimuat oleh bootstrp kelas loader, tidak peduli berapa banyak loader kelas yang ada dalam program Anda, kelas -kelas ini sebenarnya dapat dibagikan, yang menghindari kebingungan yang disebabkan oleh berbagai pemuat kelas memuat kelas yang berbeda dari nama yang sama.
Cara menyesuaikan classloader
Selain classloader yang disediakan secara default yang disebutkan di atas, Java juga memungkinkan aplikasi untuk menyesuaikan classloader. Jika Anda ingin menyesuaikan ClassLoader, kami perlu mengimplementasikannya dengan mewarisi java.lang.classloader. Selanjutnya, mari kita lihat beberapa metode penting yang perlu kita perhatikan saat menyesuaikan classloader:
1.LoadClass Metode
Metode LoadClass menyatakan
kelas publik <?> LoadClass (nama string) melempar ClassNotFoundException
Di atas adalah deklarasi prototipe dari metode LoadClass. Implementasi mekanisme delegasi induk yang disebutkan di atas sebenarnya diimplementasikan dalam metode ini. Mari kita lihat kode metode ini untuk melihat bagaimana itu mengimplementasikan delegasi orang tua.
Metode LoadClass mengimplementasikan
kelas publik <?> LoadClass (Name String) melempar ClassNotFoundException {return loadClass (name, false);}Dari yang di atas, kita dapat melihat bahwa metode LoadClass memanggil metode LoadCclass (Name, False), jadi mari kita lihat implementasi metode LoadClass lainnya.
Class LoadClass (nama string, boolean resolve)
Kelas Sinkronisasi yang Dilindungi <?> LoadClass (nama String, Boolean Resolve) melempar ClassNotFoundException {// Pertama, periksa apakah kelas telah dimuat kelas C = findloadedclass (name); // periksa apakah kelas tidak dimuat (c == null) {try {if (parent! = null) {partent) {partent. Loader ditentukan, loader induk didelegasikan untuk dimuat. } else {c = findbootstrapclass0 (name); // Jika tidak ada loader kelas induk, maka delegasi bootstrap loader untuk memuat}} catch (classnotfoundException e) {// jika masih belum ditemukan, maka aktifkan findclass dalam urutan // untuk menemukan kelas. c = findClass (name); // Jika pemuatan kelas induk tidak dimuat, itu akan dimuat melalui findClass sendiri. }} if (resolve) {resolveClass (c);} return c;}Dalam kode di atas, saya menambahkan komentar untuk melihat dengan jelas bagaimana mekanisme delegasi induk dari LoadClass bekerja. Satu hal yang perlu kita perhatikan di sini adalah bahwa kelas publik <?> LoadClass (Name String) melempar ClassNotFoundException tidak ditandai sebagai final, yang berarti bahwa kita dapat mengganti metode ini, yang berarti bahwa mekanisme delegasi induk dapat rusak. Selain itu, kami perhatikan bahwa ada metode FindClass di atas. Selanjutnya, mari kita bicara tentang apakah metode ini buruk.
2.FindClass
Kami memeriksa kode sumber java.lang.classloader dan kami menemukan bahwa implementasi FindClass adalah sebagai berikut:
Kelas Protected <?> FindClass (Name String) Melempar ClassNotFoundException {Throw New ClassNotFoundException (name);}Kita dapat melihat bahwa implementasi default dari metode ini adalah secara langsung melempar pengecualian, tetapi pada kenyataannya, metode ini diserahkan pada aplikasi kami untuk mengganti. Implementasi spesifik tergantung pada logika implementasi Anda. Anda dapat membaca dari disk atau mendapatkan aliran byte file kelas dari jaringan. Setelah mendapatkan biner kelas, Anda dapat menyerahkannya ke Defineclass untuk pemuatan lebih lanjut. Mari kita jelaskan Defineclass nanti. OK, melalui analisis di atas, kita dapat menarik kesimpulan berikut:
Ketika kita menulis classloader kita sendiri, jika kita ingin mengikuti mekanisme delegasi induk, kita hanya perlu mengganti FindClass.
3. Defineclass
Mari pertama -tama lihat kode sumber Defineclass:
Defineclass
Kelas Akhir yang Dilindungi <?> Defineclass (nama string, byte [] b, int off, int len) melempar classFormateRror {return defineclass (name, b, off, len, null);}Dari kode di atas, kita dapat melihat bahwa metode ini didefinisikan sebagai final, yang berarti bahwa metode ini tidak dapat ditimpa. Bahkan, ini juga satu -satunya entri yang tersisa bagi kami oleh JVM. Melalui entri unik ini, JVM memastikan bahwa file kelas harus mematuhi definisi kelas yang ditentukan dalam spesifikasi mesin virtual Java. Metode ini akhirnya akan memanggil metode asli untuk mengimplementasikan pemuatan kelas nyata.
OK, melalui deskripsi di atas, mari kita pikirkan tentang pertanyaan berikut:
Jika kita menulis kelas Java.lang.string sendiri, bisakah kita mengganti kelas yang memanggil JDK sendiri?
Jawabannya adalah tidak. Kami tidak dapat mencapainya. Mengapa? Saya melihat banyak penjelasan online bahwa mekanisme delegasi orang tua memecahkan masalah ini, tetapi sebenarnya tidak terlalu akurat. Karena mekanisme delegasi induk dapat rusak, Anda dapat menulis classloader untuk memuat kelas java.lang.string yang Anda tulis, tetapi Anda akan menemukan bahwa itu tidak akan berhasil memuat. Secara khusus, untuk kelas yang dimulai dengan Java.*, Implementasi JVM telah memastikan bahwa itu harus dimuat oleh bootstrp.
Skenario yang tidak mengikuti "mekanisme delegasi orang tua"
Di atas disebutkan bahwa mekanisme delegasi induk terutama untuk mewujudkan masalah interaksi kelas yang dimuat di antara classloader yang berbeda. Kelas -kelas yang dibagikan oleh semua orang diserahkan ke pemuat induk untuk dimuat, tetapi memang ada situasi di Java di mana kelas -kelas yang dimuat oleh loader kelas induk perlu menggunakan kelas yang dimuat oleh pemuat anak. Mari kita bicara tentang terjadinya situasi ini.
Ada standar SPI (ServiceProviderInterface) di Java yang menggunakan perpustakaan SPI, seperti JDBC, JNDI, dll. Kita semua tahu bahwa JDBC mengharuskan pengemudi yang disediakan oleh pihak ketiga, dan paket toples pengemudi ditempatkan di classpath aplikasi kami sendiri. API JDBC sendiri adalah bagian dari JDK yang disediakan oleh JDK, dan telah dimuat oleh Bootstrp. Jadi bagaimana cara memuat kelas implementasi yang disediakan oleh produsen pihak ketiga? Java memperkenalkan konsep pemuatan kelas konteks utas. Loader kelas utas akan mewarisi dari utas induk secara default. Jika tidak ditentukan, standarnya adalah System Class Loader (AppClassLoader). Dengan cara ini, ketika driver pihak ketiga dimuat, itu dapat dimuat melalui loader kelas konteks utas.
Selain itu, untuk mengimplementasikan OSGI loader kelas yang lebih fleksibel dan beberapa javaAppserver, ia juga merusak mekanisme delegasi induk.
Meringkaskan
Di atas adalah semua konten dari artikel ini tentang analisis kode penggunaan dan penggunaan mekanisme classloader Java. Saya harap ini akan membantu semua orang. Teman yang tertarik dapat terus merujuk ke topik terkait lainnya di situs ini. Jika ada kekurangan, silakan tinggalkan pesan untuk menunjukkannya. Terima kasih teman atas dukungan Anda untuk situs ini!