У чому різниця між Class.getResource () та ClassLoader.getResource ()?


194

Цікаво, в чому різниця між Class.getResource()і ClassLoader.getResource()?

редагувати: Я особливо хочу знати, чи є кешування на рівні файлів / директорій. Як у "чи є кешовані списки каталогів у версії Class?"

AFAIK наступні фактично повинні робити те саме, але вони не є:

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

Я виявив це, коли знайшов якийсь код генерації звітів, який створює новий файл WEB-INF/classes/із існуючого файлу в цьому каталозі. Під час використання методу з класу я міг знайти файли, які були там при розгортанні, використовуючи getClass().getResource(), але, намагаючись отримати новий файл, я отримав нульовий об'єкт. Перегляд каталогу чітко показує, що новий файл є. Назви файлів були попередньо накресленими внизу, як у "/myFile.txt".

З іншого боку, ClassLoaderверсія getResource()знайшла створений файл. З цього досвіду складається враження, що відбувається якесь кешування списку каталогів. Я правий, і якщо так, то де це документально підтверджено?

З API документації поClass.getResource()

Знаходить ресурс із заданим іменем. Правила пошуку ресурсів, пов'язаних з даним класом, реалізуються визначальним завантажувачем класу класу. Цей метод делегує завантажувача класу цього об'єкта. Якщо цей об'єкт був завантажений завантажувачем класу завантажувальної програми, метод делегується до ClassLoader.getSystemResource (java.lang.String).

Для мене це означає, що "Class.getResource насправді викликає getResource ()" власного завантажувача. Що було б те саме, що робити getClass().getClassLoader().getResource(). Але це, очевидно, немає. Невже хтось може, будь-ласка, надати мені трохи висвітлення цього питання?

Відповіді:


6

Щоб відповісти на питання, чи відбувається кешування.

Далі я дослідив цей момент, запустивши автономну програму Java, яка постійно завантажувала файл з диска методом getResourceAsStream ClassLoader. Я зміг редагувати файл, і зміни були відображені негайно, тобто файл перезавантажувався з диска без кешування.

Однак: Я працюю над проектом з декількома модулями Maven та веб-проектами, які залежать один від одного. Я використовую IntelliJ як свій IDE для збирання та запуску веб-проектів.

Я помітив, що вищезазначене, здається, більше не відповідає дійсності, тому що файл, який я завантажував, зараз випікається в банку і розгортається у залежному веб-проекті. Я помітив це лише після спроби змінити файл у моїй цільовій папці, безрезультатно. Це зробило враження, ніби відбувається кешування.


Я надто використовував Maven та IntelliJ, тому це відповідь у середовищі, яке найбільш відповідає моєму та має розумне пояснення до питання №2.
олігофрен

251

Class.getResourceможе прийняти "відносне" ім'я ресурсу, яке обробляється відносно пакету класу. Крім того, ви можете вказати "абсолютне" ім'я ресурсу, використовуючи провідну рису. Шлях ресурсів завантажувача класу завжди вважається абсолютним.

Отже, в основному рівнозначні:

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

І це так (але вони відрізняються від вищезазначених):

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

Гарна відповідь з чіткими прикладами. Хоча публікація насправді мала на меті отримати відповіді на два запитання, тепер я бачу, що друге питання наче приховане. Зовсім не впевнений у тому, як / чи варто оновити публікацію, щоб це відобразити, але що я хотів би дізнатися другий, це це (наступний коментар):
oligofren

2
Чи відбувається якесь кешування у версії Class.getResource ()? Що змусило мене повірити, що це покоління деяких звітів про яшми: ми використовуємо getClass (). GetResource ("/ aDocument.jrxml") для отримання файлу XML Jasper. Потім двійковий файл яшми створюється в тому самому каталозі. getClass (). getResource ("/ aDocument.jasper") не в змозі його знайти, хоча може чітко знайти документи на одному рівні (вхідний файл). Саме тут ClassLoader.getResource () виявився корисним, оскільки, схоже, він не використовує кешування списку каталогів. Але я не можу знайти документацію щодо цього.
олігофрен

2
@oligofren: Хм ... я б не очікував, що Class.getResource () зробить там кешування ...
Джон Скіт

1
@JonSkeet чому this.getClass().getClassLoader().getResource("/");повертає null? Це не повинно бути таким же, якthis.getClass().getClassLoader().getResource(".");
Асиф Муштак

@UnKknown: Я думаю, ви, напевно, повинні поставити нове запитання щодо цього.
Джон Скіт

22

Перший виклик шукає відносно .classфайлу, тоді як останній шукає відносно кореня classpath.

Щоб налагодити подібні проблеми, надрукую URL-адресу:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );

3
Я думаю, що «корінь завантажувача» був би більш точним, ніж «rootpath root» - просто бути вибагливим.
Джон Скіт

4
Обидва можуть шукати "абсолютні шляхи", якщо ім'я файлу передбачено "/"
oligofren

2
Цікаво ... Я потрапляю у випадок, коли getClass (). GetResource ("/ someAbsPath") повертає URL-адресу у вигляді /path/to/mylib.jar!/someAbsPath та getClass (). GetClassLoafer (). GetResource (" / someAbsPath ") return null ... Отже," root of classloader ", здається, не дуже чітко визначене поняття ...
Pierre Henry


8
@PierreHenry: getClassLoader().getResource("/...")завжди повертається null- завантажувач класів не видаляє ведучого /з шляху, тому пошук завжди не працює. Тільки getClass().getResource()обробляє старт /як абсолютний шлях відносно класового шляху.
Аарон Дігулла

17

Довелося подивитися в специфікаціях:

GetResource () класу - документація визначає різницю:

Цей метод делегує виклик своєму завантажувачу класів після внесення цих змін до імені ресурсу: якщо ім'я ресурсу починається з "/", воно не змінюється; в іншому випадку ім'я пакету є передумовним до імені ресурсу після перетворення ". до "/". Якщо цей об'єкт був завантажений завантажувачем завантаження, виклик делегується ClassLoader.getSystemResource.


2
Чи маєте ви якусь інформацію про те, чи кеширується він також у списку каталогів? Це було основною відмінністю двох методів, коли спочатку шукали вхідний файл, а потім створювали файл, використовуючи той самий каталог. Версія Class не знайшла його, версія ClassLoader (обидві, використовуючи "/file.txt").
олігофрен

11

Усі ці відповіді тут, а також відповіді в цьому запитанні дозволяють стверджувати, що завантаження абсолютних URL-адрес, як-от "/foo/bar.properties", обробляється однаково за допомогою class.getResourceAsStream(String)і class.getClassLoader().getResourceAsStream(String). Це НЕ так, принаймні, не в моїй конфігурації / версії Tomcat (зараз 7.0.40).

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

Вибачте, у мене абсолютно немає задовольняючих пояснень, але я думаю, що tomcat робить брудні хитрощі та його чорну магію із завантажувачами класів та спричиняє різницю. Я завжди користувався class.getResourceAsStream(String)в минулому і не мав жодних проблем.

PS: Я також розмістив це тут


Ця поведінка виявляється помилкою у Tomcat, яку було виправлено у версії 8. Я додав абзац про це у своїй відповіді на це запитання
LordOfThePigs

2

Class.getResourcesотримає ресурс завантажувачем класів, який завантажує об'єкт. Хоча ClassLoader.getResourceбуде отримати ресурс, використовуючи вказаний завантажувач класів.


0

Я спробував прочитати з input1.txt, який знаходився в одному з моїх пакунків разом з класом, який намагався його прочитати.

Наступні роботи:

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

System.out.println(fileName);

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

Найважливішою частиною було дзвонити, getPath()якщо ви хочете правильну назву шляху у форматі String. НЕ ВИКОРИСТОВУЙТЕ,toString() тому що він додасть додатковий текст форматування, який ВСІМНО ПОВЕРІНІТЬ ім'я файла (ви можете спробувати його і побачити друк).

Затратив на це налагодження 2 години :(



Ресурси - це не файли. Вони не можуть бути розпаковані з JAR або WAR - файлу, і якщо не FileReaderабо FileInputStreamне може бути використаний для доступу до них. Відповідь невірна.
Маркіз Лорн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.