Различные способы загрузки файла в качестве входного потока
в чем разница между:
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.
6 ответов:
есть тонкие различия, как
fileName
вы проходите интерпретируется. В принципе, у вас есть 2 различных метода:ClassLoader.getResourceAsStream()
иClass.getResourceAsStream()
. Эти два метода будут находить ресурс по-разному.In
Class.getResourceAsStream(path)
, путь интерпретируется как локальный путь к пакету класса, из которого вы его вызываете. Например вызов,String.getResourceAsStream("myfile.txt")
будет искать файл в classpath в следующем месте:"java/lang/myfile.txt"
. Если ваш путь начинается с/
, тогда будет считается абсолютным путем, и начнется поиск от корня classpath. Так зоветString.getResourceAsStream("/myfile.txt")
будет смотреть на следующее место в вашем пути к классу./myfile.txt
.
ClassLoader.getResourceAsStream(path)
будет считать все пути абсолютными путями. Так зоветString.getClassLoader().getResourceAsStream("myfile.txt")
иString.getClassLoader().getResourceAsStream("/myfile.txt")
оба будут искать файл в вашем classpath в следующем месте:./myfile.txt
.каждый раз, когда я упоминаю место в этом посте, это может быть место в самой файловой системе или внутри соответствующий файл jar, в зависимости от класса и/или загрузчика классов, из которого вы загружаете ресурс.
в вашем случае вы загружаете класс с сервера приложений, поэтому вы должны использовать
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")
событие, хотя оно должно (так как то, что указывает Javadoc). [исходный код]однако в версии 8 имя ресурса нормализуется, чтобы гарантировать, что используется абсолютная версия имени ресурса. Поэтому в Tomcat 8 два вызова, описанные выше, всегда должны возвращать один и тот же результат. [источник код]
в результате, вы должны быть очень осторожны при использовании
ClassLoader.getResourceAsStream()
илиClass.getResourceAsStream()
на версиях Tomcat ранее 8. И вы также должны иметь в виду, чтоclass.getResourceAsStream("/resource.txt")
на самом деле называетclassLoader.getResourceAsStream("resource.txt")
(ведущий/
лишен).
использовать
MyClass.class.getClassLoader().getResourceAsStream(path)
для загрузки ресурса, связанного с вашим кодом. ИспользуйтеMyClass.class.getResourceAsStream(path)
в качестве ярлыка и для ресурсов, упакованных в пакет вашего класса.использовать
Thread.currentThread().getContextClassLoader().getResourceAsStream(path)
чтобы получить ресурсы, которые являются частью клиентского кода, а не жестко привязаны к вызывающему коду. Вы должны быть осторожны с этим, поскольку загрузчик класса контекста потока может указывать на что угодно.
обычная старая Java на простой старой Java 7 и никаких других зависимостей не демонстрирует разницу...
Я поставил
file.txt
inc:\temp\
а я ставлюc:\temp\
на пути к классу.есть только один случай, когда есть разница между двумя вызовами.
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); } }
все эти ответы здесь, а также ответов в этот вопрос, предложите загрузить абсолютные URL-адреса, например " /foo / bar.свойства " обрабатывается то же самое с помощью
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: Я также разместил это здесь