Apa perbedaan antara Class.getResource () dan ClassLoader.getResource ()?

194

Saya bertanya-tanya apa perbedaan antara Class.getResource()dan ClassLoader.getResource()?

sunting: Saya terutama ingin tahu apakah ada caching yang terlibat pada level file / direktori. Seperti pada "apakah daftar direktori di-cache dalam versi Kelas?"

AFAIK pada dasarnya harus melakukan hal yang sama, tetapi tidak:

getClass().getResource() 
getClass().getClassLoader().getResource()

Saya menemukan ini ketika mengutak-atik beberapa kode pembuatan laporan yang membuat file baru WEB-INF/classes/dari file yang ada di direktori itu. Ketika menggunakan metode dari Class, saya bisa menemukan file yang ada di deployment menggunakan getClass().getResource(), tetapi ketika mencoba untuk mengambil file yang baru dibuat, saya menerima objek nol. Menjelajahi direktori dengan jelas menunjukkan bahwa file baru ada di sana. Nama file diawali dengan garis miring ke depan seperti pada "/myFile.txt".

The ClassLoaderversi getResource()di sisi lain menemukan file yang dihasilkan. Dari pengalaman ini tampaknya ada semacam caching dari daftar direktori yang terjadi. Apakah saya benar, dan jika demikian, di mana ini didokumentasikan?

Dari dokumentasi API padaClass.getResource()

Menemukan sumber daya dengan nama yang diberikan. Aturan untuk mencari sumber daya yang terkait dengan kelas yang diberikan diimplementasikan oleh pemuat kelas mendefinisikan kelas. Metode ini mendelegasikan ke pemuat kelas objek ini. Jika objek ini dimuat oleh bootstrap class loader, metode mendelegasikan ke ClassLoader.getSystemResource (java.lang.String).

Bagi saya, ini berbunyi "Class.getResource benar-benar memanggil getResource ()" classloadernya sendiri. Yang akan sama dengan melakukan getClass().getClassLoader().getResource(). Tapi jelas tidak. Bisakah seseorang tolong berikan saya iluminasi tentang hal ini?

oligofren
sumber

Jawaban:

6

Untuk menjawab pertanyaan apakah ada caching yang terjadi.

Saya menyelidiki titik ini lebih jauh dengan menjalankan aplikasi Java yang berdiri sendiri yang terus memuat file dari disk menggunakan metode getResourceAsStream ClassLoader. Saya dapat mengedit file, dan perubahan itu tercermin langsung, yaitu file itu dimuat ulang dari disk tanpa caching.

Namun: Saya sedang mengerjakan proyek dengan beberapa modul dan proyek web yang memiliki ketergantungan satu sama lain. Saya menggunakan IntelliJ sebagai IDE untuk mengkompilasi dan menjalankan proyek web.

Saya perhatikan bahwa hal di atas sepertinya tidak berlaku lagi, alasannya karena file yang saya muat sekarang dimasukkan ke dalam toples dan digunakan untuk proyek web yang tergantung. Saya hanya memperhatikan ini setelah mencoba mengubah file di folder target saya, tetapi tidak berhasil. Ini membuatnya seolah-olah ada caching yang terjadi.

mchlstckl
sumber
Saya juga menggunakan Maven dan IntelliJ, jadi ini adalah jawabannya dengan lingkungan yang paling cocok dengan saya dan memiliki penjelasan yang masuk akal untuk pertanyaan # 2.
oligofren
249

Class.getResourcedapat mengambil nama sumber daya "relatif", yang diperlakukan relatif terhadap paket kelas. Atau Anda dapat menentukan nama sumber daya "absolut" dengan menggunakan garis miring terkemuka. Jalur sumber daya Classloader selalu dianggap mutlak.

Jadi yang berikut ini pada dasarnya setara:

foo.bar.Baz.class.getResource("xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("foo/bar/xyz.txt");

Demikian juga ini (tetapi berbeda dari yang di atas):

foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
Jon Skeet
sumber
Jawaban yang bagus dengan contoh yang jelas. Meskipun posting sebenarnya dimaksudkan untuk mendapatkan jawaban atas dua pertanyaan, saya melihat sekarang bahwa pertanyaan kedua agak tersembunyi. Cukup tidak yakin bagaimana / apakah saya harus memperbarui pos untuk mencerminkan ini, tetapi apa yang ingin saya ketahui adalah yang kedua ini (komentar berikutnya):
oligofren
2
Apakah ada semacam caching yang terjadi di versi Class.getResource ()? Apa yang membuat saya percaya ini adalah pembuatan beberapa laporan jasper: Kami menggunakan getClass (). GetResource ("/ aDocument.jrxml") untuk mengambil file xml jasper. File jasper biner kemudian diproduksi di direktori yang sama . getClass (). getResource ("/ aDocument.jasper") tidak dapat menemukannya, meskipun ia dapat dengan jelas menemukan dokumen di level yang sama (file input). Di sinilah ClassLoader.getResource () terbukti membantu, karena sepertinya tidak menggunakan caching dari daftar direktori. Tetapi saya tidak dapat menemukan dokumentasi tentang ini.
oligofren
2
@oligofren: Hmm ... Saya tidak akan berharap Class.getResource () melakukan caching di sana ...
Jon Skeet
1
@JonSkeet mengapa this.getClass().getClassLoader().getResource("/");mengembalikan null? Seharusnya tidak sama denganthis.getClass().getClassLoader().getResource(".");
Asif Mushtaq
@ Tidak Tahu: Saya pikir Anda mungkin harus mengajukan pertanyaan baru tentang itu.
Jon Skeet
22

Panggilan pertama mencari relatif ke .classfile sedangkan pencarian terakhir relatif terhadap root classpath.

Untuk men-debug masalah seperti itu, saya mencetak URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Aaron Digulla
sumber
3
Saya pikir "root classloader" akan lebih akurat daripada "root classpath" - hanya untuk pilih-pilih.
Jon Skeet
4
Keduanya dapat mencari "path absolut" jika nama file diawali dengan "/"
oligofren
2
Menarik ... Saya menemukan kasus di mana getClass (). GetResource ("/ someAbsPath") mengembalikan URL dalam jenis /path/to/mylib.jar!/someAbsPath dan getClass (). GetClassLoafer (). GetResource (". GetResource (") / someAbsPath ") return null ... Jadi" root of classloader "tampaknya bukan gagasan yang didefinisikan dengan sangat baik ...
Pierre Henry
8
@PierreHenry: getClassLoader().getResource("/...")selalu kembali null- classloader tidak menghapus yang memimpin /dari jalan, sehingga pencarian selalu gagal. Hanya getClass().getResource()menangani permulaan /sebagai jalur absolut relatif terhadap classpath.
Aaron Digulla
17

Harus mencarinya dalam spesifikasi:

GetResource () - dokumentasi menyatakan perbedaan:

Metode ini mendelegasikan panggilan ke pemuat kelasnya, setelah melakukan perubahan pada nama sumber daya: jika nama sumber daya dimulai dengan "/", itu tidak berubah; jika tidak, nama paket didahului dengan nama sumber setelah mengonversi "." ke "/". Jika objek ini dimuat oleh bootstrap loader, panggilan tersebut didelegasikan ke ClassLoader.getSystemResource.

Bernd Elkemann
sumber
2
Apakah Anda memiliki info apakah cache daftar direktori juga? Ini adalah perbedaan utama antara kedua metode ketika ketika pertama kali mencari file input, dan kemudian membuat file menggunakan itu di direktori yang sama. Versi Class tidak menemukannya, versi ClassLoader melakukannya (keduanya menggunakan "/ file.txt").
oligofren
11

Semua jawaban di sekitar sini, serta jawaban dalam pertanyaan ini , menyarankan bahwa memuat URL absolut, seperti "/foo/bar.properties" diperlakukan sama oleh class.getResourceAsStream(String)dan class.getClassLoader().getResourceAsStream(String). Ini BUKAN kasusnya, setidaknya tidak dalam konfigurasi / versi Tomcat saya (saat ini 7.0.40).

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

Maaf, saya sama sekali tidak memiliki penjelasan yang memuaskan, tapi saya kira kucing jantan melakukan trik kotor dan sihir hitamnya dengan classloader dan menyebabkan perbedaan. Saya selalu menggunakan class.getResourceAsStream(String)di masa lalu dan tidak punya masalah.

PS: Saya juga memposting ini di sini

Tim Büthe
sumber
Perilaku ini tampaknya menjadi bug di Tomcat yang diperbaiki dalam versi 8. Saya menambahkan paragraf tentang itu dalam jawaban
LordOfThePigs
2

Class.getResourcesakan mengambil sumber daya oleh classloader yang memuat objek. Sementara ClassLoader.getResourceakan mengambil sumber daya menggunakan classloader yang ditentukan.

lwpro2
sumber
0

Saya mencoba membaca dari input1.txt yang ada di dalam salah satu paket saya bersama dengan kelas yang mencoba membacanya.

Karya-karya berikut:

String fileName = FileTransferClient.class.getResource("input1.txt").getPath();

System.out.println(fileName);

BufferedReader bufferedTextIn = new BufferedReader(new FileReader(fileName));

Bagian terpenting adalah menelepon getPath()jika Anda ingin nama jalur yang benar dalam format String. JANGAN GUNAKANtoString() karena akan menambahkan beberapa teks format tambahan yang akan BENAR-BENAR MESS UP fileName (Anda dapat mencobanya dan melihat hasil cetaknya).

Menghabiskan 2 jam men-debug ini ... :(

Kevin Lee
sumber
Bagaimana dengan Class.getResourceAsStream () ?
Lu55
Sumber bukan file. Mereka mungkin tidak dibongkar dari file JAR atau WAR, dan jika tidak a FileReaderatau FileInputStreamtidak dapat digunakan untuk mengaksesnya. Jawabannya tidak benar.
Marquis of Lorne