«1. Обзор

В этом руководстве мы рассмотрим различные способы внедрения содержимого ресурса, содержащего текст, в виде строки в наши bean-компоненты Spring.

Мы рассмотрим поиск ресурса и чтение его содержимого.

Также мы покажем, как разделить загруженные ресурсы между несколькими bean-компонентами. Мы покажем это с помощью аннотаций, связанных с внедрением зависимостей, хотя того же можно добиться с помощью внедрения на основе XML и объявления bean-компонентов в файле свойств XML.

2. Использование ресурсов

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

Давайте рассмотрим несколько способов получения экземпляра Resource для ресурсов в пути к классам.

2.1. Использование ResourceLoader

Мы можем использовать класс ResourceLoader, если мы предпочитаем использовать ленивую загрузку:

ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource("classpath:resource.txt");

Мы также можем внедрить ResourceLoader в наш компонент с помощью @Autowired:

@Autowired
private ResourceLoader resourceLoader;

2.2 Использование @Resource ~~ ~ Мы можем внедрить Resource непосредственно в bean-компонент Spring с помощью @Value:

3. Преобразование из Resource в String

@Value("classpath:resource.txt")
private Resource resource;

Получив доступ к Resource, мы должны иметь возможность прочитать его в String. Давайте создадим служебный класс ResourceReader со статическим методом asString, который сделает это за нас.

Во-первых, мы должны получить InputStream:

Наш следующий шаг — взять этот InputStream и преобразовать его в строку. Мы можем использовать собственный метод FileCopyUtils#copyToString Spring:

InputStream inputStream = resource.getInputStream();

Есть много других способов добиться этого, например, используя copyToString класса Spring StreamUtils

public class ResourceReader {

    public static String asString(Resource resource) {
        try (Reader reader = new InputStreamReader(resource.getInputStream(), UTF_8)) {
            return FileCopyUtils.copyToString(reader);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    // more utility methods
}

Давайте также создадим еще один служебный метод readFileToString, который будет получать Resource для пути и вызовите метод asString, чтобы преобразовать его в строку.

4. Добавление класса конфигурации

public static String readFileToString(String path) {
    ResourceLoader resourceLoader = new DefaultResourceLoader();
    Resource resource = resourceLoader.getResource(path);
    return asString(resource);
}

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

Мы можем добиться более аккуратного решения, внедрив содержимое ресурса в один или несколько компонентов Spring при загрузке контекста приложения. Таким образом, мы можем скрыть детали реализации для чтения ресурса из различных bean-компонентов, которым необходимо использовать это содержимое.

4.1. Использование Bean-компонента, содержащего строку ресурса

@Configuration
public class LoadResourceConfig {

    // Bean Declarations
}

Объявим bean-компоненты для хранения содержимого ресурса в классе @Configuration:

Давайте теперь внедрим зарегистрированные bean-компоненты в поля, добавив аннотацию @Autowired:

@Bean
public String resourceString() {
    return ResourceReader.readFileToString("resource.txt");
}

~ ~~ В этом случае мы используем аннотацию @Qualifier и имя компонента, так как нам может понадобиться внедрить несколько полей одного типа — String.

public class LoadResourceAsStringIntegrationTest {
    private static final String EXPECTED_RESOURCE_VALUE = "...";  // The string value of the file content

    @Autowired
    @Qualifier("resourceString")
    private String resourceString;

    @Test
    public void givenUsingResourceStringBean_whenConvertingAResourceToAString_thenCorrect() {
        assertEquals(EXPECTED_RESOURCE_VALUE, resourceString);
    }
}

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

5. Использование SpEL

Наконец, давайте посмотрим, как мы можем использовать язык выражений Spring для описания кода, необходимого для загрузки файла ресурсов непосредственно в поле нашего класса.

Давайте воспользуемся аннотацией @Value для вставки содержимого файла в поле resourceStringUsingSpel:

Здесь мы вызвали ResourceReader#readFileToString, описывающую расположение файла с помощью «classpath:» — путь с префиксом внутри нашей аннотации @Value.

public class LoadResourceAsStringIntegrationTest {
    private static final String EXPECTED_RESOURCE_VALUE = "..."; // The string value of the file content

    @Value(
      "#{T(com.baeldung.loadresourceasstring.ResourceReader).readFileToString('classpath:resource.txt')}"
    )
    private String resourceStringUsingSpel;

    @Test
    public void givenUsingSpel_whenConvertingAResourceToAString_thenCorrect() {
        assertEquals(EXPECTED_RESOURCE_VALUE, resourceStringUsingSpel);
    }
}

Чтобы уменьшить объем кода в SpEL, мы создали вспомогательный метод в классе ResourceReader, который использует Apache Commons FileUtils для доступа к файлу по указанному пути:

6. Заключение

public class ResourceReader {
    public static String readFileToString(String path) throws IOException {
        return FileUtils.readFileToString(ResourceUtils.getFile(path), StandardCharsets.UTF_8);
    }
}

В этом руководстве мы рассмотрели некоторые способы преобразования ресурса в строку.

Прежде всего, мы увидели, как создать ресурс для доступа к файлу и как читать из ресурса в строку.

«Далее мы также показали, как скрыть реализацию загрузки ресурсов и разрешить совместное использование содержимого строки между bean-компонентами путем создания квалифицированных bean-компонентов в @Configuration, позволяющих автоматически связывать строки.

Наконец, мы использовали SpEL, который обеспечивает компактное и быстрое решение, хотя и требует пользовательской вспомогательной функции, чтобы не допустить чрезмерного усложнения.

Как всегда, код примеров можно найти на GitHub

«