Ресурс Java из класса против потока
В чем разница между
getClass().getResource("some-resource-file.txt")
против
Thread.currentThread().getContextClassLoader().getResource("some-resource-file.txt")
У меня есть ресурсы в src/test/resources, и я пытаюсь получить к ним доступ из модульного теста. Это типичная структура каталогов в стиле maven.
Я ожидал, что оба будут вести себя одинаково. Но это не так. GetClass(). GetResource() не извлекает ресурс, где, как из потока, я могу получить ресурс.
Так чем они отличаются?
2 ответа
Существует особый случай запуска первого класса (именно поэтому вы должны объявить метод main() как статический с массивом строк в качестве аргумента). Как только этот класс загружен и запущен, будущие попытки загрузки классов будут выполняться загрузчиком классов. В самом простом случае загрузчик классов создает плоское пространство имен тел классов, на которые ссылается строковое имя. Каждый класс в Java использует собственный загрузчик классов для загрузки других классов. Так что если ClassA.class
Рекомендации ClassB.class
затем ClassB
должен быть на пути к классам ClassLoader
из ClassA
или его родители.
Контекст потока ClassLoader
особенный в том, что это текущий ClassLoader
для текущего запущенного потока. Это полезно в средах с несколькими классами загрузчиков. Объект может быть создан из класса в ClassLoader C
а затем перешли к теме, принадлежащей ClassLoader D
, В этом случае объект должен использовать Thread.currentThread().getContextClassLoader()
напрямую, если он хочет загрузить ресурсы, которые не доступны сами по себе ClassLoader
,
Допустим, вы разрабатываете библиотеку, а jar библиотеки помещается в путь к классу веб-контейнера.
Теперь допустим, что веб-приложение, использующее эту библиотеку, развернуто в контейнере.
Веб-приложение будет иметь собственный загрузчик классов, использующий WEB-INF/classes и WEB-INF/lib/*. Jar в качестве пути к классам. И контейнер для каждого запроса, поступающего в ваше веб-приложение, устанавливает текущий загрузчик классов потока в загрузчик классов пути к классам.
Когда код вашей библиотеки использует getClass().getResource()
, он загрузит ресурс, используя загрузчик классов, используемый для загрузки классов библиотеки. Таким образом, он будет использовать загрузчик классов контейнера и, таким образом, будет использовать ресурсы в jar вашей библиотеки и в других библиотеках, используемых для запуска контейнера.
Если код вашей библиотеки использует Thread.currentThread().getContextClassLoader()
вместо этого, чтобы загрузить ресурс, он будет использовать загрузчик классов, связанный с текущим потоком, и, таким образом, загрузит ресурсы из загрузчика классов веб-приложения, ища ресурс в WEB-INF/classes и в jar-файлах внутри WEB-INF/lib.
Последний может быть тем, что вы хотите. Например, если вы разрабатываете библиотеку журналов (пожалуйста, не надо), регистратор сможет читать разные файлы конфигурации для каждого веб-приложения, вместо того, чтобы иметь единую конфигурацию, совместно используемую всеми веб-приложениями.
Относительно того, как два метода ищут ресурсы, все они, наконец, делегируют ClassLoader для загрузки ресурса. Но загрузка его через класс будет относиться к относительным путям относительно вызванного класса, тогда как загрузка его через ClassLoader ожидает путь, начинающийся в корне дерева пакетов. Предположим, ваш класс находится в пакете com.foo
, затем
MyClass.class.getResource("hello.txt")
эквивалентно
MyClass.class.getResource("/com/foo/hello.txt")
и эквивалентно
MyClass.class.getClassLoader().getResource("com/foo/hello.txt");