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

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

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

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


person oligofren    schedule 07.07.2011    source источник


Ответы (9)


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

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

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

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

person mchlstckl    schedule 15.05.2012
comment
Я тоже использовал Maven и IntelliJ, так что это ответ в среде, которая наиболее близко соответствует моей и имеет разумное объяснение вопроса №2. - person oligofren; 10.01.2014

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");
person Jon Skeet    schedule 07.07.2011
comment
Хороший ответ с наглядными примерами. Хотя пост на самом деле был предназначен для получения ответов на два вопроса, теперь я вижу, что второй вопрос как бы скрыт. Совершенно не уверен, как / следует ли мне обновить сообщение, чтобы отразить это, но во-вторых, я хотел бы знать следующее (следующий комментарий): - person oligofren; 07.07.2011
comment
Есть ли какое-то кеширование в версии Class.getResource ()? Что заставило меня поверить, что это генерация некоторых отчетов о jasper: мы используем getClass (). GetResource (/aDocument.jrxml) для получения файла jasper xml. Затем в том же каталоге создается двоичный файл яшмы. getClass (). getResource (/aDocument.jasper) не может его найти, хотя явно может находить документы на том же уровне (входной файл). Именно здесь ClassLoader.getResource () оказался полезным, поскольку, похоже, он не использует кеширование списка каталогов. Но я не могу найти по этому поводу документации. - person oligofren; 07.07.2011
comment
@oligofren: Хм ... Я бы не ожидал, что Class.getResource () будет там кэшировать ... - person Jon Skeet; 07.07.2011
comment
@JonSkeet, почему this.getClass().getClassLoader().getResource("/"); возвращает null? Он не должен быть таким же, как this.getClass().getClassLoader().getResource("."); - person Asif Mushtaq; 17.02.2018
comment
@UnKnown: Думаю, вам стоит задать новый вопрос по этому поводу. - person Jon Skeet; 17.02.2018
comment
@JonSkeet с ведущей косой чертой работает безупречно для извлечения ресурсов из корня тестовых ресурсов! - person Gaurav; 13.05.2019

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

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

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
person Aaron Digulla    schedule 07.07.2011
comment
Я думаю, что корень загрузчика классов будет более точным, чем корень пути к классам - просто для разборчивости. - person Jon Skeet; 07.07.2011
comment
Оба могут искать абсолютные пути, если перед именем файла стоит / - person oligofren; 07.07.2011
comment
Интересно ... Я попал в случай, когда getClass (). GetResource (/ someAbsPath) возвращает URL-адрес в типе /path/to/mylib.jar!/someAbsPath и getClass (). GetClassLoafer (). GetResource (/ someAbsPath) return null ... Итак, корень загрузчика классов кажется не очень четко определенным понятием ... - person Pierre Henry; 16.04.2013
comment
см. stackoverflow.com/questions/13269556/ - person Pierre Henry; 16.04.2013
comment
@PierreHenry: getClassLoader().getResource("/...") всегда возвращает null - загрузчик классов не удаляет начальный / из пути, поэтому поиск всегда терпит неудачу. Только getClass().getResource() обрабатывает начальный / как абсолютный путь относительно пути к классам. - person Aaron Digulla; 16.04.2013

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

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

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

person Bernd Elkemann    schedule 07.07.2011
comment
Есть ли у вас какая-либо информация о том, кэширует ли он также список каталогов? Это было основным различием между двумя методами при первом поиске входного файла, а затем создании файла с его использованием в том же каталоге. Версия Class не нашла его, версия ClassLoader нашла (обе с использованием /file.txt). - person oligofren; 07.07.2011

Все эти ответы здесь, а также ответы в этом вопросе предполагают загрузку абсолютных 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!

Извините, у меня нет абсолютно удовлетворительного объяснения, но я предполагаю, что кот делает грязные трюки и свою черную магию с загрузчиками классов и вызывает разницу. Раньше я всегда использовал class.getResourceAsStream(String), и у меня не было никаких проблем.

PS: Я также разместил это здесь

person Tim Büthe    schedule 19.11.2013

Class.getResources получит ресурс загрузчиком классов, который загружает объект. В то время как ClassLoader.getResource будет извлекать ресурс, используя указанный загрузчик классов.

person engineer    schedule 14.08.2014

Начиная с Java 9 существует ловушка с ClassLoader#getResource при запуске по пути к модулю. Из-за этого я бы никогда не использовал ClassLoader#getResource в новом коде.

Если ваш код находится в названном модуле и вы используете ClassLoader#getResource, ваш код может не получить ресурс , даже если ресурс находится в том же модуле. Это очень неожиданное поведение.

Я сам испытал это и был очень удивлен этой разнице между Class#getResource и ClassLoader#getResource. Однако это полностью определенное поведение в соответствии с javadoc:

Кроме того, за исключением особого случая, когда имя ресурса заканчивается на .class, этот метод будет находить ресурсы только в пакетах с именованными модулями, когда пакет открывается безоговорочно (даже если вызывающий этот метод находится в тот же модуль, что и ресурс).

Javadoc (выделено мной)

person A248    schedule 04.04.2021

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

Следующие работы:

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

System.out.println(fileName);

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

Самой важной частью было вызов getPath(), если вы хотите получить правильное имя пути в строковом формате. НЕ ИСПОЛЬЗУЙТЕ toString(), потому что это добавит некоторый дополнительный текст форматирования, который ПОЛНОСТЬЮ ЗАМЕТИТ имя файла (вы можете попробовать это и посмотреть распечатку).

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

person Kevin Lee    schedule 11.03.2014
comment
А как насчет класса .getResourceAsStream ()? - person Lu55; 15.01.2018
comment
Ресурсы - это не файлы. Они могут быть не распакованы из файла JAR или WAR, и в противном случае FileReader или FileInputStream не могут быть использованы для доступа к ним. Ответ неверный. - person user207421; 03.07.2018

Еще один более эффективный способ - просто использовать @Value.

@Value("classpath:sss.json")
private Resource resource;

и после этого вы можете просто получить файл таким образом

File file = resource.getFile();

person thelastworm    schedule 06.01.2021
comment
Это действительно не имеет отношения к обсуждению загрузчиков классов. Никого не волнует, как загрузить сюда файл, но какая разница. - person oligofren; 07.01.2021