Стандартный класс java.net.URL и стандартные обработчики различных префиксов URL в Java, к сожалению, не достаточно подходят для обеспечения доступа ко всем низкоуровневым ресурсам. Например, не существует стандартизированной реализации URL, которую можно использовать для получения доступа к ресурсу, который нужно получить из пути классов или относительно ServletContext. Хотя можно зарегистрировать новые обработчики для специализированных префиксов URL (аналогично существующим обработчикам для таких префиксов, как http:), это обычно довольно сложно, а интерфейсу URL все еще не будет хватает некоторых желательных функций, таких как метод проверки существования ресурса, на который указывают.

Интерфейс Resource

Интерфейс Resource фреймворка Spring, расположенный в пакете org.springframework.core.io. предназначен для абстрагирования доступа к низкоуровневым ресурсам. В следующем листинге кратко представлен интерфейс Resource. Более подробную информацию см. в javadoc по Resource.

public interface Resource extends InputStreamSource {
    boolean exists();
    boolean isReadable();
    boolean isOpen();
    boolean isFile();
    URL getURL() throws IOException;
    URI getURI() throws IOException;
    File getFile() throws IOException;
    ReadableByteChannel readableChannel() throws IOException;
    long contentLength() throws IOException;
    long lastModified() throws IOException;
    Resource createRelative(String relativePath) throws IOException;
    String getFilename();
    String getDescription();
}

Как видно из определения интерфейса Resource, он расширяет интерфейс InputStreamSource. В следующем листинге показано определение интерфейса InputStreamSource:

public interface InputStreamSource {
    InputStream getInputStream() throws IOException;
}

Некоторыми из наиболее важных методов интерфейса Resource являются:

  • getInputStream(): Находит и открывает ресурс, возвращая InputStream для считывания из ресурса. Предполагается, что каждый вызов возвращает новый InputStream. Ответственность за закрытие потока лежит на вызывающем коде.

  • exists(): Возвращает булево значение boolean, указывающее, существует ли данный ресурс в физической форме.

  • isOpen(): Возвращает булево значение boolean, указывающее, представляет ли данный ресурс дескриптор (хэндл) с открытым потоком. Если true, то InputStream не может быть считан несколько раз и должен быть считан только единожды, а затем закрыт, чтобы избежать утечки ресурсов. Возвращает false для всех стандартных реализаций ресурсов, за исключением InputStreamResource.

  • getDescription(): Возвращает описание для этого ресурса, которое будет использоваться для вывода ошибок при работе с ресурсом. Часто это полностью уточненное имя файла или фактический URL ресурса.

Другие методы позволяют получить фактический URL или объект File, представляющий ресурс (если базовая реализация совместима и поддерживает эту функциональность).

Некоторые реализации интерфейса Resource также реализуют расширенный интерфейс WritableResource для ресурса, который поддерживает запись в него.

Сам Spring широко использует абстракцию Resource в качестве типа аргумента во многих сигнатурах методов, когда требуется какой-либо ресурс. Другие методы в некоторых API-интерфейсах Spring (например, конструкторы различных реализаций ApplicationContext) принимают String, которая без дополнений или в простом виде используется для создания Resource, соответствующего данной реализации контекста, или при помощи специальных префиксов на пути к String позволяет вызывающему коду задать создание и использование конкретной реализации Resource.

Хотя интерфейс Resource часто используется в Spring и самим Spring, на самом деле его очень удобно использовать в качестве общего служебного класса в собственном коде для получения доступа к ресурсам, даже если ваш код не знает о других частях Spring и не реализует их. Хотя это и привязывает ваш код к Spring, на самом деле такого рода привязка осуществляет только с этим небольшим набором служебных классов, который служит более функциональной заменой URL и может считаться эквивалентным любой другой библиотеке, которую вы бы использовали для этой цели.

Абстракция Resource не заменяет функциональность. По мере возможности она оборачивается. Например, UrlResource оборачивает URL и использует обернутый URL для выполнения своей задачи.

Встроенные реализации Resource

Spring включает в себя несколько встроенных реализаций Resource:

  • UrlResource

  • ClassPathResource

  • FileSystemResource

  • PathResource

  • ServletContextResource

  • InputStreamResource

  • ByteArrayResource

Полный список реализаций Resource, доступных в Spring, можно найти в разделе "Все известные реализации классов" в javadoc по Resource.

UrlResource

UrlResource оборачивает java.net.URL и может быть использован для получения доступа к любому объекту, доступ к которому в обычных обеспечивается с помощью URL, например, к файлам, целевому элементу HTTPS, целевому элементу FTP и другому. Все URL-адреса имеют стандартизированное String представление, поэтому для обозначения одного типа URL-адреса от другого используются соответствующие стандартизированные префиксы. К ним относятся file: для доступа к путям файловой системы, https: для доступа к ресурсам по протоколу HTTPS, ftp: для доступа к ресурсам по протоколу FTP и другие.

UrlResource создается кодом Java путем явного использования конструктора UrlResource, но зачастую создается и неявно, если вызывать метод API-интерфейса, который принимает аргумент String, предназначенный для представления пути. В последнем случае PropertyEditor класса JavaBeans в конечном итоге решает, какой тип Resource создать. Если строка пути содержит известный (имеется в виду редактору свойств) префикс (например, classpath:), то создается соответствующий специализированный Resource для этого префикса. Однако, если он не распознает префикс, он принимает строку за стандартную строку URL и создает UrlResource.

ClassPathResource

Этот класс представляет ресурс, который нужно получить из classpath. Для загрузки ресурсов используется либо загрузчик контекстных классов потока, либо данный загрузчик классов, либо данный класс.

Эта реализация Resource поддерживает разрешение как java.io.File, если ресурс пути классов находится в файловой системе, но не для ресурсов classpath, которые находятся в jar и не были развернуты (движком сервлета или какой-либо другой средой) в файловую систему. Чтобы решить эту проблему, различные реализации Resource всегда имеют поддержку выполнения разрешения в виде java.net.URL.

ClassPathResource создается в коде Java с помощью конструктора ClassPathResource, но зачастую создается и неявно, если вызывать метод API-интерфейса, который принимает аргумент String, предназначенный для представления пути. Для последнего случая PropertyEditor класса JavaBeans распознает специальный префикс classpath: в строке пути и в этом случае создает ClassPathResource.

FileSystemResource

Это реализация Resource, предназначенная для дескрипторов экземпляров класса java.io.File. Он также поддерживает обработку java.nio.file.Path, применяя стандартные для Spring преобразования путей на основе строк, но выполняя все операции через API-интерфейс java.nio.file.Files. Для чистой поддержки на основе java.nio.path.Path используйте вместо этого PathResource. FileSystemResource поддерживает разрешение и в виде File, и в виде URL.

PathResource

Это реализация интерфейса Resource, предназначенная для дескрипторов java.nio.file.Path, выполняющая все операции и преобразования через API-интерфейс Path. Данная реализация поддерживает разрешение как File и как URL, а также реализует расширенный интерфейс WritableResource. PathResource фактически является чистой альтернативой FileSystemResource, основанной на java.nio.path.Path, с иной логикой работы createRelative.

ServletContextResource

Это реализация Resource для ресурсов ServletContext, которая интерпретирует относительные пути в корневом каталоге соответствующего веб-приложения.

Она всегда поддерживает потоковый доступ и доступ по URL, но также предоставляет доступ по java.io.File только в том случае, если архив веб-приложения развернут, а ресурс физически находится в файловой системе. Будет ли он развернут в файловой системе, будет ли он доступен непосредственно из JAR или откуда-то еще, например, из базы данных (что вполне возможно), на самом деле зависит от контейнера сервлетов.

InputStreamResource

InputStreamResource – это реализация Resource для данного InputStream. Её следует использовать только в том случае, если не применима конкретная реализация Resource. В частности, отдавайте предпочтение ByteArrayResource или любой из реализаций Resource, основанных на файлах, где это возможно.

В отличие от других реализаций Resource, это дескриптор для уже открытого ресурса. Поэтому он возвращает true из функции isOpen(). Не используйте её, если вам необходимо где-то хранить дескриптор ресурса или если вам нужно считать поток несколько раз.

ByteArrayResource

Это реализация Resource, предназначенная для заданного байтового массива. Создает ByteArrayInputStream для заданного байтового массива.

Она полезна для загрузки содержимого из любого заданного байтового массива без необходимости использования одноразового InputStreamResource.