Учебные материалы

Урок 3: Работа с ресурсами

Этот урок освещает работу с ресурсами и основан на оригинальной документации §6. Resources.

Что вам потребуется

Настройка проекта

Прежде чем вы начнете изучать этот урок, вам необходимо создать класс lessons.starter.ResourceStarter.

Введение

Стандартный Java-класс java.net.URL и стандартные обработчки различных URL префиксов к сожалению не во всём достаточно пригодны для доступа к низкоуровневым ресурсам. К примеру, нет стандартизированных реализаций URL, которые могут быть использованы для доступа к ресурсам, которые необходимо получить из classpath, либо ServletContext. Можно зарегистрировать новые обработчики для конкретных URL префиксов, но это довольно сложная задача, а интерфейс URL все ещё недостаточно функционален.

Spring предоставляет интерфейс Resource, более совместимый для низкоуровнего доступа.

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isOpen();

    URL getURL() throws IOException;

    File getFile() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();

    //...
}
public interface InputStreamSource {

    InputStream getInputStream() throws IOException;

}

Интерфейс Resource используется и в самом Spring в качестве типа аргументов во многих методах, где необходимы ресурсы. Другие методы принимают в качестве аргументов строковые значения, которые в конечном счете используются для получения ресурсов. Важно понимать, что реализации интерфейса Resource не заменяют функциональность, а являются "оберткой". Например, URLResource является "оберткой" для URL.

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

Из реализаций интерфейса Resource, которые входят в состав Spring, стоит выделить следующие:

  • UrlResource - является оберткой для java.net.URL и может быть использован для доступа к любому объекту по его URL, например, файлы, HTTP или FTP ресурс и др., либо через строковое представление с соответветствующими префиксами classpath:, file:, http:, ftp: и др.
  • ClassPathResource - представляет ресурс, который может быть получен из classpath и поддерживает ресурсы как java.io.File, если они расположены в файловой системе, а не в jar
  • FileSystemResource - реализация для обработки java.io.File. Поддерживает как File, так и URL
  • ServletContextResource - реализация для обработки ServletContext ресурсов относительно корневой директории web-приложения. Поддерживает потоковый и к URL доступ. Доступ к java.io.File возможен, когда web-приложение развернуто и ресурс физически находится в файловой системе
  • InputStreamResource - реализация для обработки InputStream. Должен использоваться только тогда, когда нет доступных соответствующих реализаций интерфейса Resource или нет возможности использовать более предпочтительный вариант ByteArrayResource
  • ByteArrayResource - реализация для обработки массива байтов. Полезен для загрузки контента из любого массива байтов, не прибегая к использованию InputStreamResource

Получение ресурсов

ResourceLoader

Интерфейс ResourceLoader предназначен для реализации возврата, т.е. загрузки, экземпляров Resource.

public interface ResourceLoader {

    Resource getResource(String location);

}

Все контексты приложения реализуют данный интерфейс. Когда вы вызываете метод getResource() в определенном контексте приложения без указания префикса, то вы получите тот тип Resource, который соответствует контексту. В качестве демонстрации, можно запустить метод src/main/java/lessons/starter/ResourceStarter#printClassResourcesByContextNoPrefix(). В методе src/main/java/lessons/starter/ResourceStarter#printClassResourcesByContextWithPrefix() на консоль отбражаются подобные результаты, но ресурс передается уже с префиксом.

src/main/lessons/starter/ResourceStarter.java

public class ResourceStarter {

    public static final Logger LOGGER = LogManager.getLogger(ResourceStarter.class);

    public static void main(String[] args) throws IOException {
        LOGGER.info("Starting resources...");
        ResourceStarter starter = new ResourceStarter();
        starter.printClassResourcesByContextNoPrefix();
        starter.printClassResourcesByContextWithPrefix();
    }

    public void printClassResourcesByContextNoPrefix() {
        LOGGER.info("*******printClassResourcesByContextNoPrefix**********");
        ApplicationContext context = new AnnotationConfigApplicationContext();
        Resource resource = context.getResource("resources/log4j.properties");
        LOGGER.info("AnnotationConfigApplicationContext: " + resource.getClass().getSimpleName());

        context = new ClassPathXmlApplicationContext();
        resource = context.getResource("resources/log4j.properties");
        LOGGER.info("ClassPathXmlApplicationContext: " + resource.getClass().getSimpleName());

        context = new FileSystemXmlApplicationContext();
        resource = context.getResource("resources/log4j.properties");
        LOGGER.info("FileSystemXmlApplicationContext: " + resource.getClass().getSimpleName());
        LOGGER.info("****************************************************");
    }

    public void printClassResourcesByContextWithPrefix() {
        LOGGER.info("*******printClassResourcesByContextWithPrefix*******");
        ApplicationContext context = new AnnotationConfigApplicationContext();
        Resource resource = context.getResource("file:resources/log4j.properties");
        LOGGER.info("file: " + resource.getClass().getSimpleName());
        resource = context.getResource("http://spring.io/img/favicon.png");
        LOGGER.info("http: " + resource.getClass().getSimpleName());
        resource = context.getResource("classpath:resources/log4j.properties");
        LOGGER.info("classpath: " + resource.getClass().getSimpleName());
        LOGGER.info("****************************************************");
    }
}

ResourceLoaderAware

Интерфейс ResourceLoaderAware обеспечивает объект ссылкой на ResourceLoader.

public interface ResourceLoaderAware {

    void setResourceLoader(ResourceLoader resourceLoader);
}

Если класс реализует интерфейс ResourceLoaderAware и развернут в контексте приложения, например, как бин, то при его сборке контекст приложения будет вызывать метод setResourceLoader() и передавать соответствующий ResourceLoader, в зависимости от типа контекста.

Получение ресурса по его пути

Если ваш контекст ClassPathXmlApplicationContext, то вы можете получить ресурс по его пути с указанием префикса classpath: или без него с учетом его местоположения в classpath. Если же вам необходимо получить ресурс по его полному пути, то вам необходимо указать префикс file:, либо ваш контекст должен быть типа FileSystemXmlApplicationContext. В качестве демонстрации, можно запустить метод src/main/java/lessons/starter/ResourceStarter#resourcePath().

src/main/lessons/starter/ResourceStarter.java

public class ResourceStarter {

    public static final Logger LOGGER = LogManager.getLogger(ResourceStarter.class);

    public static void main(String[] args) throws IOException {
        //...
        starter.resourcePath();
    }

    public void resourcePath() throws IOException {
        LOGGER.info("*****************resourcePath()*********************");
        LOGGER.info("context = new ClassPathXmlApplicationContext()");
        ApplicationContext context = new ClassPathXmlApplicationContext();
        Resource resource = context.getResource("log4j.properties");
        LOGGER.info("log4j.properties exist: " + resource.getFile().exists());
//        resource = context.getResource("src/main/resources/log4j.properties");
//        LOGGER.info("log4j.properties exist: " + resource.getFile().exists()); //IOException
        resource = context.getResource("file:src/main/resources/log4j.properties");
        LOGGER.info("file:src/main/resources/log4j.properties exist: " + resource.getFile().exists());
        resource = context.getResource("log/log.txt");
        LOGGER.info("log/log.txt exist: " + resource.getFile().exists());
        resource = context.getResource("classpath:log/log.txt");
        LOGGER.info("classpath:log/log.txt exist: " + resource.getFile().exists());
        resource = context.getResource("file:log.txt");
        LOGGER.info("file:log.txt exist: " + resource.getFile().exists());
        resource = context.getResource("file:log/log.txt");
        LOGGER.info("file:log/log.txt exist: " + resource.getFile().exists());
        resource = context.getResource("file:src/main/resources/log/log.txt");
        LOGGER.info("file:src/main/resources/log/log.txt exist: " + resource.getFile().exists());
        LOGGER.info("context = new FileSystemXmlApplicationContext()");
        context = new FileSystemXmlApplicationContext();
        resource = context.getResource("log4j.properties");
        LOGGER.info("log4j.properties exist: " + resource.getFile().exists());
        resource = context.getResource("src/main/resources/log4j.properties");
        LOGGER.info("src/main/resources/log4j.properties exist: " + resource.getFile().exists());
        resource = context.getResource("log/log.txt");
        LOGGER.info("log/log.txt exist: " + resource.getFile().exists());
        resource = context.getResource("classpath:log/log.txt");
        LOGGER.info("classpath:log/log.txt exist: " + resource.getFile().exists());
        resource = context.getResource("file:log.txt");
        LOGGER.info("file:log.txt exist: " + resource.getFile().exists());
        resource = context.getResource("file:log/log.txt");
        LOGGER.info("file:log/log.txt exist: " + resource.getFile().exists());
        LOGGER.info("****************************************************");
    }
}

Итог

Несмотря на небольшой объем урока, вы познакомились с основами доступа к ресурсам встроенными в Spring средствами. В соответствии с тем, что уроки не предусматривают работу с XML-конфигурациями, а только c Java-конфигурациями, не были рассмотрены варианты указания различных шаблонов в конструктор XML-конфигурациии, но вам ничто не мешает при необходимости ознакомиться с соответствующими разделами документации.

comments powered by Disqus