Bingung
Di masa lalu, ketika melihat kode sumber, saya selalu menemukan kode dalam kerangka kerja menggunakan thread.currentThread.getContextClassLoader () untuk mendapatkan loader kelas konteks dari utas saat ini, dan menggunakan loader kelas konteks ini untuk memuat kelas.
Ketika kami biasanya menulis kode dalam suatu program, ketika kami ingin memuat kelas secara dinamis, kami biasanya menggunakan class.forname () untuk memuat kelas yang kami butuhkan. Misalnya, hal yang paling umum adalah ketika kami memprogram JDBC, kami menggunakan class.forname () untuk memuat driver JDBC.
Coba {return class.forname ("oracle.jdbc.driver.oracledRiver");} catch (ClassNotFoundException e) {// Skip}Jadi mengapa ketika kami menggunakan class.forname () untuk memuat kelas, jika kelas tidak dapat ditemukan, kami masih mencoba memuat kelas menggunakan class loader yang diperoleh dengan thread.currentThread.getContextLoader ()? Misalnya, kita dapat menemukan kode berikut:
coba {return class.forname (className);} catch (classNotFoundException e) {// skip} classLoader ctxclassloader = thread.currentThread (). getContextClassLoader (); if (ctxclassloader! = null) {coba {clazz = ctxclassloader.loadClass (className); } catch (classnotfoundException e) {// lewati}}Bagian tebal di sini adalah menggunakan loader yang diperoleh dengan thread.currentThread.getContextLoader () untuk memuat kelas. Jelas, class loader yang digunakan ketika class.forname () memuat kelas mungkin berbeda dari loader kelas yang diperoleh oleh thread.currentThread.getContextLoader (). Jadi mengapa perbedaannya terjadi?
Java Class Loader
Sebelum memahami mengapa loader kelas ini diperoleh dengan thread.currentThread.getContextLoader () digunakan, mari kita pahami pertama class loader (classloader) yang digunakan dalam JVM.
Ada tiga jenis classloader di JVM secara default:
Bootstrap Class Loader
Bootstrap Class Loader Class Loader adalah loader kelas yang dibangun ke dalam JDK, yang digunakan untuk memuat kelas di dalam JDK. Loader kelas bootstrap digunakan untuk memuat kelas di bawah $ java_home/jre/lib di JDK, seperti kelas dalam paket rt.jar. Loader kelas bootstrap adalah bagian dari JVM dan umumnya ditulis dalam kode asli.
Loader kelas ekstensi
Loader kelas Loader Kelas Ekstensi terutama digunakan untuk memuat kelas dalam paket ekstensi JDK. Secara umum, paket di bawah $ java_home/lib/ext dimuat melalui loader kelas ini, dan kelas di bawah paket ini pada dasarnya dimulai dengan Javax.
Loader kelas sistem
Loader kelas Loader Kelas Sistem juga disebut Loader Kelas Aplikasi (AppClassLoader). Seperti namanya, loader kelas ini digunakan untuk memuat kode aplikasi yang biasanya ditulis pengembang. Loader kelas sistem digunakan untuk memuat kelas tingkat aplikasi yang disimpan di jalur ClassPath.
Kode berikut mencantumkan tiga loader kelas ini:
kelas publik MainClass {public static void main (string [] args) {System.out.println (integer.class.getClassLoader ()); System.out.println (logging.class.getClassLoader ()); System.out.println (mainClass.class.getClassLoader ()); }}Tempat mendapatkan bootstrap class loader mengembalikan nilai nol selamanya
null # bootstrap kelas loader sun.misc.launcher$xtclassloader@5e2de80c # class ekstensi loader sun.misc.launcher$appclassloader@18b4aaC2 # System Class Loader
Model delegasi orang tua
Tiga jenis loader yang diperkenalkan di atas tidak terisolasi, mereka memiliki hubungan hierarkis:
Tiga loader kelas bekerja bersama melalui hubungan hierarkis ini dan bertanggung jawab atas pemuatan kelas bersama. Model hierarkis di atas disebut model "delegasi induk" dari class loader. Model delegasi induk mensyaratkan bahwa semua loader kelas harus memiliki loader induk kecuali untuk loader kelas bootstrap tingkat atas. Ketika class loader memuat kelas, pertama -tama periksa apakah ada kelas dalam cache yang telah dimuat. Jika tidak, maka loader induk didelegasikan untuk memuat kelas terlebih dahulu. Loader induk melakukan pekerjaan yang sama dengan loader anak sebelumnya sampai permintaan mencapai loader kelas bootstrap tingkat atas. Jika loader induk tidak dapat memuat kelas yang diperlukan, maka pemuat anak akan mencoba memuat kelas dengan sendirinya. Ini bekerja mirip dengan yang berikut:
Kita dapat menggunakan kode di ClassLoader di JDK untuk melihat implementasi mekanisme delegasi induk. Kode ini diimplementasikan di ClassLoader.LoadClass ()
Kelas yang diputar <?> 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) {resolveClass (c); } return c; }Salah satu keuntungan menggunakan delegasi orang tua untuk mengatur loader kelas adalah aman. Jika kita mendefinisikan kelas string sendiri, kami berharap dapat mengganti kelas string ini dengan implementasi java.lang.string di java default.
Kami meletakkan file kelas dari kelas string yang kami terapkan di jalur ClassPath. Ketika kami menggunakan class loader untuk memuat kelas string yang kami terapkan, pertama, class loader akan mendelegasikan permintaan ke loader induk, dan melalui lapisan demi lapis, loader kelas bootstrap akhirnya akan memuat jenis string dalam paket RT.jar, dan kemudian mengembalikannya ke kami sepanjang jalan. Dalam proses ini, loader kelas kami mengabaikan kelas string yang kami tempatkan di classpath.
Jika mekanisme delegasi induk tidak diadopsi, loader kelas sistem dapat menemukan file kelas string di jalur ClassPath dan memuatnya ke dalam program, menyebabkan implementasi string di JDK ditimpa. Oleh karena itu, metode kerja loader kelas ini memastikan bahwa program Java dapat berjalan dengan aman dan stabil sampai batas tertentu.
Thread Class Class Loader
Pembicaraan di atas tentang begitu banyak konten terkait loader kelas, tetapi masih tidak membicarakan topik hari ini, loader kelas konteks utas.
Pada titik ini, kita sudah tahu bahwa Java menyediakan tiga jenis loader dan bekerja dalam konser sesuai dengan mekanisme delegasi orang tua yang ketat. Di permukaan, tampaknya sempurna, tetapi mekanisme delegasi orang tua yang ketat inilah yang menyebabkan beberapa keterbatasan saat memuat kelas.
Ketika kerangka kerja kami yang lebih mendasar perlu menggunakan kelas tingkat aplikasi, kami hanya dapat menggunakan kelas ini jika kelas ini dimuat ketika loader kelas yang digunakan oleh kerangka kerja kami saat ini dapat dimuat. Dengan kata lain, kami tidak dapat menggunakan loader kelas dari loader kelas saat ini. Keterbatasan ini disebabkan oleh mekanisme delegasi induk, karena delegasi permintaan pemuatan kelas satu arah.
Meskipun tidak ada banyak kasus, masih ada permintaan seperti itu. Layanan JNDI yang khas. JNDI menyediakan antarmuka untuk meminta sumber daya, tetapi implementasi spesifik diimplementasikan oleh produsen yang berbeda. Pada saat ini, kode JNDI dimuat oleh loader kelas bootstrap JVM, tetapi implementasi spesifik adalah kode selain JDK yang disediakan oleh pengguna, sehingga hanya dapat dimuat oleh pemuat kelas sistem atau loader kelas yang ditentukan pengguna lainnya. Di bawah mekanisme delegasi induk, JNDI tidak dapat memperoleh implementasi SPI JNDI.
Untuk mengatasi masalah ini, loader kelas konteks utas diperkenalkan. SetContextClassLoader () dari java.lang.thread kelas setContextClassLoader () (jika tidak diatur, itu akan diwarisi dari utas induk secara default. Jika program belum mengaturnya, itu akan default ke loader kelas sistem). Dengan loader kelas konteks utas, aplikasi dapat melewati loader kelas yang digunakan oleh aplikasi ke kode yang menggunakan loader kelas tingkat atas melalui java.lang.thread.setContextClassLoader (). Misalnya, layanan JNDI di atas dapat menggunakan metode ini untuk mendapatkan loader kelas yang dapat memuat implementasi SPI dan mendapatkan kelas implementasi SPI yang diperlukan.
Dapat dilihat bahwa memperkenalkan loader kelas berulir sebenarnya merupakan penghancuran mekanisme delegasi induk, tetapi memberikan fleksibilitas dalam pemuatan kelas.
Menyelesaikan keraguan
Kembali ke awal, untuk memuat kelas yang diterapkan oleh pengguna di luar kerangka kerja, kelas -kelas ini mungkin tidak dimuat melalui loader kelas yang digunakan oleh kerangka kerja. Untuk mem -bypass model delegasi induk dari class loader, thread.getContextClassLoader () digunakan untuk memuat kelas -kelas ini.
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.