В чем разница между Class.getResource () и ClassLoader.getResource ()?

194

Интересно какая разница между Class.getResource()а ClassLoader.getResource()?

редактировать: я особенно хочу знать, если какое-либо кэширование на уровне файлов / каталогов. Как в "кэшируются ли списки каталогов в версии класса?"

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(). Но это явно не так. Может ли кто-нибудь дать мне некоторое представление об этом?

oligofren
источник

Ответы:

6

Чтобы ответить на вопрос, происходит ли кеширование.

Я исследовал этот вопрос далее, запустив отдельное Java-приложение, которое непрерывно загружало файл с диска, используя метод getResourceAsStream ClassLoader. Я смог отредактировать файл, и изменения были немедленно отражены, т. Е. Файл был перезагружен с диска без кэширования.

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

Я заметил, что вышеупомянутое, похоже, больше не соответствует действительности, потому что файл, который я загружал, теперь запекается в банку и развертывается в зависимом веб-проекте. Я заметил это только после попытки изменить файл в моей целевой папке, но безрезультатно. Это создавало впечатление, что кеширование происходит.

mchlstckl
источник
Я тоже использовал Maven и IntelliJ, так что это ответ в среде, которая наиболее близко соответствует моей и имеет разумное объяснение для вопроса № 2.
oligofren
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 ()? Что заставило меня поверить, что это генерация некоторых отчетов jasper: Мы используем getClass (). GetResource ("/ aDocument.jrxml"), чтобы получить XML-файл jasper. Затем создается двоичный файл яшмы в же каталоге . getClass (). getResource ("/ aDocument.jasper") не может найти его, хотя он может четко находить документы на том же уровне (входной файл). Вот где ClassLoader.getResource () оказался полезным, так как кажется, что он не использует кэширование списка каталогов. Но я не могу найти документацию по этому вопросу.
oligofren
2
@oligofren: Хм ... я бы не ожидал, что Class.getResource () сделает там кеширование ...
Джон Скит
1
@JonSkeet зачем this.getClass().getClassLoader().getResource("/");возвращать ноль ? Это не должно быть так же, какthis.getClass().getClassLoader().getResource(".");
Асиф Муштак
@UnKnown: я думаю, тебе стоит задать новый вопрос об этом.
Джон Скит
22

Первый вызов выполняет поиск относительно .classфайла, а последний - относительно корня пути к классам.

Для устранения подобных проблем я печатаю URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Аарон Дигулла
источник
3
Я думаю, что «корень загрузчика классов» будет более точным, чем «корень classpath» - просто чтобы быть разборчивым.
Джон Скит
4
Оба могут искать «абсолютные пути», если перед именем файла стоит «/»
oligofren
2
Интересно ... Я попал в случай, когда getClass (). GetResource ("/ someAbsPath") возвращает URL в типе /path/to/mylib.jar!/someAbsPath и getClass (). GetClassLoafer (). GetResource (" / someAbsPath ") return null ... Так что" корень загрузчика классов ", кажется, не очень хорошо определенное понятие ...
Пьер Генри
см. stackoverflow.com/questions/13269556/…
Пьер Генри
8
@PierreHenry: getClassLoader().getResource("/...")всегда возвращается null- загрузчик классов не удаляет ведущий /из пути, поэтому поиск всегда дает сбой. Только getClass().getResource()обрабатывает начало /как абсолютный путь относительно пути к классам.
Аарон Дигулла
17

Пришлось посмотреть это в спецификации:

Класс getResource () - документация утверждает разницу:

Этот метод делегирует вызов своему загрузчику классов после внесения этих изменений в имя ресурса: если имя ресурса начинается с "/", оно не изменяется; в противном случае имя пакета добавляется к имени ресурса после преобразования "." в "/". Если этот объект был загружен загрузчиком начальной загрузки, вызов делегируется ClassLoader.getSystemResource.

Бернд Элькеманн
источник
2
Есть ли у вас какая-либо информация о том, кэшируется ли она и в каталоге? Это было основным различием между этими двумя методами, когда сначала просматривали входной файл, а затем создавали файл, используя его в том же каталоге. Версия Class не нашла его, версия ClassLoader нашла (оба использовали "/file.txt").
oligofren
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бы получить ресурс, используя указанный загрузчик классов.

lwpro2
источник
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() потому что это добавит некоторый дополнительный форматирующий текст, который ПОЛНОСТЬЮ НЕПРАВИЛЬНО ИСПОЛЬЗУЕТ fileName (вы можете попробовать его и посмотреть распечатку).

Потратил 2 часа на отладку этого ... :(

Кевин Ли
источник
Как насчет Class.getResourceAsStream () ?
Lu55
Ресурсы не являются файлами. Они не могут быть распакованы из файла JAR или WAR, а если нет FileReaderили FileInputStreamне могут быть использованы для доступа к ним. Ответ не верный.
Маркиз Лорн