Между тем, ничего сложного тут нет. Надо только понимать принципы.
Прежде всего, грузить ресурсы по абсолютному адресу на диске – занятие бесперспективное. Думаю, сами прекрасно понимаете, почему – убрали файл с диска, и «прощай ресурс». Всё свое надо носить с собой.
Второй вариант, который я часто вижу, – загрузка ресурса из jar-файла. Но тут очень часто делается одна ошибка – ресурс пытаются грузить через класс
java.io.File
. При том, что этот класс предназначен только для работы с файловыми системами.Хотя сама идея правильная. Нужный ресурс действительно необходимо поместить в jar-файл. Надо только понимать, как его оттуда загрузить. Вот об этом я и расскажу.
Для загрузки ресурса служат методы
java.lang.Class.getResource(String)
, java.lang.Class.getResourceAsStream(Stri ng)
, java.lang.ClassLoader.getResource(String)
и java.lang.ClassLoader.getResourceAsStrea m(String)
. Методы Class
-а делегируют вызовы ClassLoader
-у.getResource(String)
по имени ресурса возвращает java.net.URL
, через который можно получить этот ресурс. getResourceAsStream(String)
, как нетрудно догадаться, возвращает java.io.InputStream
, через который ресурс можно прочитать.Имя ресурса представляет собой путь к ресурсу. Есть одна существенная тонкость, а именно – как оно интерпретируется.
Имя может быть абсолютным и относительным. Внешнее отличие – абсолютное имя начинается с символа '/'. В первом случае ресурс ищется относительно корня classpath. Т.е. берутся все пути и jar-файлы, входящие в classpath, и ресурс ищется относительно совокупности этих точек. Если же имя относительное – к нему в начало приписывается путь, полученный из пакета текущего класса. Далее поиск ведется как в случае абсолютного имени.
Проще это понять на примерах. Пусть у нас задан
classpath
: c:\work\myproject\classes;c:\lib\lib.jar
. Код примера находится в классе ru.skipy.test.ResourceLoadingTest
.Пример 1. Мы используем конструкцию
getClass().getResource("/images/logo.png" )
. Поскольку имя начинается с символа '/' – оно считается абсолютным. Поиск ресурса происходит следующим образом:- К пути из classpath
c:\work\myproject\classes
приписывается имя ресурса/images/logo.png
, в результате чего ищется файлc:\work\myproject\classes\images\logo.pn
. Если файл найден – поиск прекращается. Иначе:g - В jar-файле
c:\lib\lib.jar
ищется файл/images/logo.png
, причем поиск ведется от корня jar-файла.
Пример 2. Мы используем конструкцию
getClass().getResource("res/data.txt")
. Поскольку имя не начинается с символа '/' – оно считается относительным. Поиск ресурса происходит следующим образом:- К пути из classpath
c:\work\myproject\classes
приписывается текущий пакет класса, где находится код, –/ru/skipy/test
, – и далее имя ресурсаres/data.txt
, в результате чего ищется файлc:\work\myproject\classes\ru\skipy\test\r
. Если файл найден – поиск прекращается. Иначе:es\data.txt - В jar-файле
c:\lib\lib.jar
ищется файл/ru/skipy/test/res/data.txt
(имя пакета текущего класса плюс имя ресурса), причем поиск ведется от корня jar-файла.
Ну и для того, чтобы ресурс был найден, необходимо обеспечить его существование. Т.е. при сборке приложения (упаковке его в jar-файл) позаботиться о том, чтобы ресурс тоже попал куда надо. Как это сделать – зависит от технологии сборки, многие IDE умеют копировать ресурсы при сборке в ту же точку, куда кладут и скомпилированные классы. При использовании
ant
это надо предусмотреть в явном виде.Вот тут можно скачать полностью рабочий пример, иллюстрирующий оба типа загрузки: http://lj.skipy.ru/samples/resource_load.zip. Ресурсы – изображение и текст – располагаются в отдельной директории, при сборке попадают в jar-файл и грузятся один по абсолютному, другой по относительному имени. Пример собирается и запускается через
ant
, командой ant run
он запускается из директории сборки build/classes/
, командой ant run-jar
– из собранного jar-файла.Вот, где-то так. Вопросы? Комментарии?