Apa perbedaan antara pemuat kelas konteks thread dan pemuat kelas normal?
Yaitu, jika Thread.currentThread().getContextClassLoader()
dan getClass().getClassLoader()
mengembalikan objek loader kelas yang berbeda, mana yang akan digunakan?
Apa perbedaan antara pemuat kelas konteks thread dan pemuat kelas normal?
Yaitu, jika Thread.currentThread().getContextClassLoader()
dan getClass().getClassLoader()
mengembalikan objek loader kelas yang berbeda, mana yang akan digunakan?
Jawaban:
Setiap kelas akan menggunakan classloadernya sendiri untuk memuat kelas lain. Jadi, jika ClassA.class
referensi ClassB.class
maka ClassB
perlu berada di jalan setapak classloader ClassA
, atau orang tuanya.
Classloader konteks thread adalah classloader saat ini untuk utas saat ini. Objek dapat dibuat dari kelas di ClassLoaderC
dan kemudian diteruskan ke utas yang dimiliki oleh ClassLoaderD
. Dalam hal ini objek perlu digunakan Thread.currentThread().getContextClassLoader()
secara langsung jika ingin memuat sumber daya yang tidak tersedia pada classloadernya sendiri.
ClassA.class
rujukan ClassB.class
" itu?
Ini tidak menjawab pertanyaan awal, tetapi karena pertanyaan itu berperingkat tinggi dan ditautkan untuk ContextClassLoader
pertanyaan apa pun , saya pikir penting untuk menjawab pertanyaan terkait kapan pemuat kelas konteks harus digunakan. Jawaban singkat: jangan pernah gunakan pemuat kelas konteks ! Tetapi atur getClass().getClassLoader()
ketika Anda harus memanggil metode yang tidak memiliki ClassLoader
parameter.
Ketika kode dari satu kelas meminta untuk memuat kelas lain, pemuat kelas yang benar untuk digunakan adalah pemuat kelas yang sama dengan kelas pemanggil (yaitu, getClass().getClassLoader()
). Ini adalah cara kerja 99,9% dari waktu karena ini adalah apa yang JVM lakukan sendiri saat pertama kali Anda membangun sebuah instance dari kelas baru, memanggil metode statis, atau mengakses bidang statis.
Ketika Anda ingin membuat kelas menggunakan refleksi (seperti ketika deserializing atau memuat kelas bernama yang dapat dikonfigurasi), perpustakaan yang melakukan refleksi harus selalu menanyakan aplikasi pemuat kelas mana yang akan digunakan, dengan menerima ClassLoader
sebagai parameter dari aplikasi. Aplikasi (yang tahu semua kelas yang perlu dibangun) harus lulus getClass().getClassLoader()
.
Cara lain untuk mendapatkan pemuat kelas tidak benar. Jika penggunaan perpustakaan hacks seperti Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
, atau sun.reflect.Reflection.getCallerClass()
itu adalah bug yang disebabkan oleh kekurangan dalam API. Pada dasarnya, Thread.getContextClassLoader()
ada hanya karena siapa pun yang merancang ObjectInputStream
API lupa untuk menerima ClassLoader
sebagai parameter, dan kesalahan ini telah menghantui komunitas Java hingga hari ini.
Yang mengatakan, banyak banyak kelas JDK menggunakan satu dari beberapa peretasan untuk menebak beberapa loader kelas untuk digunakan. Beberapa menggunakan ContextClassLoader
(yang gagal ketika Anda menjalankan aplikasi yang berbeda pada kumpulan utas bersama, atau ketika Anda meninggalkan ContextClassLoader null
), beberapa berjalan tumpukan (yang gagal ketika penelepon langsung dari kelas itu sendiri perpustakaan), beberapa menggunakan loader sistem kelas (yang baik-baik saja, asalkan didokumentasikan hanya menggunakan kelas di CLASSPATH
) atau bootstrap kelas loader, dan beberapa menggunakan kombinasi teknik-teknik di atas yang tidak dapat diprediksi (yang hanya membuat hal-hal lebih membingungkan). Ini telah menyebabkan banyak tangisan dan kertakan gigi.
Saat menggunakan API seperti itu, pertama-tama, cobalah untuk menemukan kelebihan metode yang menerima pemuat kelas sebagai parameter . Jika tidak ada metode yang masuk akal, cobalah mengatur ContextClassLoader
sebelum panggilan API (dan mengatur ulang setelah itu):
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
// call some API that uses reflection without taking ClassLoader param
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
Ada artikel di javaworld.com yang menjelaskan perbedaan => ClassLoader mana yang harus Anda gunakan
(1)
Classloaders konteks thread memberikan pintu belakang di sekitar skema delegasi classloading.
Ambil JNDI sebagai contoh: nyali diimplementasikan oleh kelas bootstrap di rt.jar (dimulai dengan J2SE 1.3), tetapi kelas inti JNDI ini dapat memuat penyedia JNDI yang diimplementasikan oleh vendor independen dan berpotensi digunakan dalam -kelas jalur aplikasi. Skenario ini memerlukan classloader induk (yang primordial dalam kasus ini) untuk memuat kelas yang terlihat oleh salah satu classloader anak-nya (sistem satu, misalnya). Delegasi J2SE yang normal tidak berfungsi, dan solusinya adalah membuat kelas inti JNDI menggunakan thread konteks loader, sehingga secara efektif "tunneling" melalui hierarki classloader dalam arah yang berlawanan dengan delegasi yang tepat.
(2) dari sumber yang sama:
Kebingungan ini mungkin akan tetap dengan Java untuk beberapa waktu. Ambil API J2SE apa pun dengan pemuatan sumber daya dinamis apa pun dan coba tebak strategi pemuatan yang digunakannya. Berikut adalah contohnya:
- JNDI menggunakan classloaders konteks
- Class.getResource () dan Class.forName () menggunakan classloader saat ini
- JAXP menggunakan classloaders konteks (pada J2SE 1.4)
- java.util.ResourceBundle menggunakan classloader pemanggil saat ini
- Penangan protokol URL yang ditentukan melalui properti sistem java.protocol.handler.pkgs dilihat hanya di bootstrap dan classloaders sistem
- Java Serialization API menggunakan classloader saat ini dari pemanggil secara default
bootstrap
pemuat kelas induk yang ditetapkan sebagai pemuat kelas konteks tetapi system
pemuat kelas classpath anak yang Thread
sedang disiapkan. The JNDI
kelas kemudian pastikan untuk menggunakan Thread.currentThread().getContextClassLoader()
untuk memuat kelas implementasi JNDI tersedia di classpath.
Menambahkan ke @David Roussel jawaban, kelas dapat dimuat oleh beberapa kelas loader.
Mari kita mengerti cara kerja pemuat kelas .
Dari blog javin paul di javarevisited:
ClassLoader
mengikuti tiga prinsip.
Kelas dimuat di Jawa, saat dibutuhkan. Misalkan Anda memiliki kelas khusus aplikasi yang disebut Abc.class, permintaan pertama untuk memuat kelas ini akan datang ke Application ClassLoader yang akan mendelegasikan kepada induknya Extension ClassLoader yang selanjutnya mendelegasikan ke loader kelas Primordial atau Bootstrap
Bootstrap ClassLoader bertanggung jawab untuk memuat file kelas JDK standar dari rt.jar dan merupakan induk dari semua pemuat kelas di Jawa. Pemuat kelas bootstrap tidak memiliki orang tua.
Extension ClassLoader mendelegasikan permintaan pemuatan kelas ke induknya, Bootstrap dan jika tidak berhasil, memuat kelas dari direktori jre / lib / ext direktori atau direktori lain yang diarahkan oleh properti sistem java.ext.dirs
Pemuat kelas sistem atau Aplikasi dan bertanggung jawab untuk memuat kelas aplikasi khusus dari variabel lingkungan CLASSPATH, opsi baris -classpath atau -cp, baris atribut Path-file dari file Manifest di dalam JAR.
Pemuat kelas aplikasi adalah anak dari Extension ClassLoader dan diimplementasikan oleh sun.misc.Launcher$AppClassLoader
kelas.
CATATAN: Kecuali pemuat kelas Bootstrap , yang diimplementasikan dalam bahasa asli sebagian besar dalam bahasa C, semua pemuat kelas Java diimplementasikan menggunakan java.lang.ClassLoader
.
Menurut prinsip visibilitas, Child ClassLoader dapat melihat kelas dimuat oleh Parent ClassLoader tetapi sebaliknya tidak benar.
Menurut prinsip ini, kelas yang dimuat oleh Orang Tua tidak boleh dimuat oleh Child ClassLoader lagi
ClassB
harus di jalan setapakClassA
loader (atauClassA
orang tua loader)? MungkinkahClassA
loader overrideloadClass()
, sehingga dapat memuat dengan suksesClassB
bahkan ketikaClassB
tidak ada di classpath?