Вопрос-ответ

Different ways of loading a file as an InputStream

Различные способы загрузки файла в виде входного потока

В чем разница между:

InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)

и

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)

и

InputStream is = this.getClass().getResourceAsStream(fileName)

Когда каждый из них более уместен для использования, чем другие?

Файл, который я хочу прочитать, находится в classpath как мой класс, который считывает файл. Мой класс и файл находятся в одном jar, упакованы в EAR-файл и развернуты в WebSphere 6.1.

Переведено автоматически
Ответ 1

Существуют небольшие различия в том, как интерпретируется fileName передаваемый вами файл. По сути, у вас есть 2 разных метода: ClassLoader.getResourceAsStream() и Class.getResourceAsStream(). Эти два метода по-разному определяют местоположение ресурса.

В Class.getResourceAsStream(path) путь интерпретируется как локальный путь к пакету класса, из которого вы его вызываете. Например, вызывая, String.class.getResourceAsStream("myfile.txt") будет искать файл в вашем classpath в следующем расположении: "java/lang/myfile.txt". Если ваш путь начинается с /, то он будет считаться абсолютным путем и поиск начнется с корня classpath . Таким образом, при вызове String.class.getResourceAsStream("/myfile.txt") будет просматриваться следующее местоположение в вашем пути к классу ./myfile.txt.

ClassLoader.getResourceAsStream(path) все пути будут рассматриваться как абсолютные пути. Таким образом, вызов String.class.getClassLoader().getResourceAsStream("myfile.txt") и String.class.getClassLoader().getResourceAsStream("/myfile.txt") оба будут искать файл в вашем classpath в следующем расположении: ./myfile.txt.

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

В вашем случае вы загружаете класс с сервера приложений, поэтому вам следует использовать Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) вместо this.getClass().getClassLoader().getResourceAsStream(fileName). this.getClass().getResourceAsStream() также будет работать.

Прочтите эту статью для получения более подробной информации об этой конкретной проблеме.


Предупреждение для пользователей Tomcat 7 и ниже

В одном из ответов на этот вопрос говорится, что мое объяснение кажется неверным для Tomcat 7. Я попытался осмотреться, чтобы понять, почему это так.

Итак, я просмотрел исходный код Tomcat WebAppClassLoader для нескольких версий Tomcat. Реализация findResource(String name) (которая в конечном итоге отвечает за создание URL-адреса запрашиваемого ресурса) практически идентична в Tomcat 6 и Tomcat 7, но отличается в Tomcat 8.

В версиях 6 и 7 реализация не пытается нормализовать имя ресурса. Это означает, что в этих версиях classLoader.getResourceAsStream("/resource.txt") может не выдавать того же результата, что и classLoader.getResourceAsStream("resource.txt") event, хотя должно (поскольку это указано в Javadoc). [исходный код]

Однако в версии 8 имя ресурса нормализовано, чтобы гарантировать, что используется абсолютная версия имени ресурса. Следовательно, в Tomcat 8 два вызова, описанные выше, всегда должны возвращать один и тот же результат. [исходный код]

В результате вы должны быть особенно осторожны при использовании ClassLoader.getResourceAsStream() or Class.getResourceAsStream() в версиях Tomcat более ранних, чем 8. И вы также должны иметь в виду, что class.getResourceAsStream("/resource.txt") на самом деле вызывает classLoader.getResourceAsStream("resource.txt") (начало / удалено).

Ответ 2

Используйте MyClass.class.getClassLoader().getResourceAsStream(path) для загрузки ресурса, связанного с вашим кодом. Используйте MyClass.class.getResourceAsStream(path) как ярлык и для ресурсов, упакованных в пакет вашего класса.

Используйте Thread.currentThread().getContextClassLoader().getResourceAsStream(path) для получения ресурсов, которые являются частью клиентского кода, а не жестко привязаны к вызывающему коду. С этим следует быть осторожным, поскольку загрузчик класса контекста потока может указывать на что угодно.

Ответ 3

Простая старая Java на простой старой Java 7 и без каких-либо других зависимостей демонстрирует разницу...

Я вставил file.txt в c:\temp\ и я вставил c:\temp\ в classpath .

Есть только один случай, когда есть разница между двумя вызовами.

class J {

public static void main(String[] a) {
// as "absolute"

// ok
System.err.println(J.class.getResourceAsStream("/file.txt") != null);

// pop
System.err.println(J.class.getClassLoader().getResourceAsStream("/file.txt") != null);

// as relative

// ok
System.err.println(J.class.getResourceAsStream("./file.txt") != null);

// ok
System.err.println(J.class.getClassLoader().getResourceAsStream("./file.txt") != null);

// no path

// ok
System.err.println(J.class.getResourceAsStream("file.txt") != null);

// ok
System.err.println(J.class.getClassLoader().getResourceAsStream("file.txt") != null);
}
}
Ответ 4

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

2024-02-06 02:35 java