Spring Integration Java DSL

Spring Integration Java DSL

1. Вступление

В этом руководстве мы узнаем о Spring Integration Java DSL для создания интеграции приложений.

Мы возьмем интеграцию с перемещением файлов, встроенную вIntroduction to Spring Integration, и воспользуемся DSL.

2. зависимости

Spring Integration Java DSL является частьюSpring Integration Core.

Итак, мы можем добавить эту зависимость:


    org.springframework.integration
    spring-integration-core
    5.0.6.RELEASE

А для работы с нашим приложением для перемещения файлов нам также понадобитсяSpring Integration File:


    org.springframework.integration
    spring-integration-file
    5.0.6.RELEASE

3. Spring Integration Java DSL

До Java DSL пользователи настраивали компоненты Spring Integration в XML.

В DSL представлены некоторые свободно работающие компоновщики, из которых мы можем легко создать законченный конвейер интеграции Spring исключительно на Java.

Итак, допустим, мы хотели создать канал, который преобразует все данные, поступающие по конвейеру.

В прошлом мы могли бы сделать:



И теперь мы можем вместо этого сделать:

@Bean
public IntegrationFlow upcaseFlow() {
    return IntegrationFlows.from("input")
      .transform(String::toUpperCase)
      .get();
}

4. Приложение для перемещения файлов

Чтобы начать интеграцию с перемещением файлов, нам понадобятся несколько простых строительных блоков.

4.1. Интеграционный процесс

Первым строительным блоком, который нам нужен, является поток интеграции, который мы можем получить от билдераIntegrationFlows :

IntegrationFlows.from(...)

Сканированиеfrom может иметь несколько типов, но в этом руководстве мы рассмотрим только три:

  • MessageSourceсс

  • MessageChannels и

  • Stringсс

Мы скоро поговорим обо всех трех.

После того, как мы вызвалиfrom, нам теперь доступны некоторые методы настройки:

IntegrationFlow flow = IntegrationFlows.from(sourceDirectory())
  .filter(onlyJpgs())
  .handle(targetDirectory())
  // add more components
  .get();

В конечном итогеIntegrationFlows всегда будет создавать экземплярIntegrationFlow, which is the final product of any Spring Integration app.

This pattern of taking input, performing the appropriate transformations, and emitting the results is fundamental to all Spring Integration apps.

4.2. Описание источника ввода

Во-первых, чтобы переместить файлы, нам нужно указать нашему потоку интеграции, где он должен их искать, и для этого нам понадобитсяMessageSource:

@Bean
public MessageSource sourceDirectory() {
  // .. create a message source
}

Проще говоря,MessageSource - это место, откудаmessages can come that are external to the application.

В частности, нам нужно что-то, что можетadapt этот внешний источник в представлении обмена сообщениями Spring. И поскольку этотadaptation ориентирован наinput, их часто называютInput Channel Adapters.

Зависимостьspring-integration-file  дает нам адаптер входного канала, который отлично подходит для нашего случая использования:FileReadingMessageSource:

@Bean
public MessageSource sourceDirectory() {
    FileReadingMessageSource messageSource = new FileReadingMessageSource();
    messageSource.setDirectory(new File(INPUT_DIR));
    return messageSource;
}

Здесь нашFileReadingMessageSource будет читать каталог, заданныйINPUT_DIR, и создаст из негоMessageSource.

Давайте укажем это как наш источник в синвокацииIntegrationFlows.from :

IntegrationFlows.from(sourceDirectory());

4.3. Настройка источника входного сигнала

Теперь, если мы думаем об этом как о долгоживущем приложении,we’ll probably want to be able to notice files as they come in, а не просто о перемещении файлов, которые уже существуют при запуске.

Чтобы облегчить это,from может также использовать дополнительныйconfigurers в качестве дополнительной настройки источника ввода:

IntegrationFlows.from(sourceDirectory(), configurer -> configurer.poller(Pollers.fixedDelay(10000)));

В этом случае мы можем сделать наш входной источник более устойчивым, сказав Spring Integration опрашивать этот источник - в данном случае нашу файловую систему - каждые 10 секунд.

И, конечно же, это не относится только к нашему источнику ввода файла, мы могли бы добавить этот опросчик к любомуMessageSource.

4.4. Фильтрация сообщений из источника ввода

Далее, предположим, что мы хотим, чтобы наше приложение для перемещения файлов перемещало только определенные файлы, например файлы изображений с расширениемjpg.

Для этого мы можем использоватьGenericSelector:

@Bean
public GenericSelector onlyJpgs() {
    return new GenericSelector() {

        @Override
        public boolean accept(File source) {
          return source.getName().endsWith(".jpg");
        }
    };
}

Итак, давайте снова обновим наш процесс интеграции:

IntegrationFlows.from(sourceDirectory())
  .filter(onlyJpgs());

Илиbecause this filter is so simple, we could have instead defined it using a lambda:

IntegrationFlows.from(sourceDirectory())
  .filter(source -> ((File) source).getName().endsWith(".jpg"));

4.5. Обработка сообщений с помощью активаторов услуг

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

Service Activators  - это то, к чему мы обращаемся, когда думаем о выводах в Spring Integration.

Давайте воспользуемся активатором службыFileWritingMessageHandler изspring-integration-file:

@Bean
public MessageHandler targetDirectory() {
    FileWritingMessageHandler handler = new FileWritingMessageHandler(new File(OUTPUT_DIR));
    handler.setFileExistsMode(FileExistsMode.REPLACE);
    handler.setExpectReply(false);
    return handler;
}

Здесь нашFileWritingMessageHandler will записывает каждую полученную полезную нагрузкуMessage вOUTPUT_DIR.

Опять же, давайте обновим:

IntegrationFlows.from(sourceDirectory())
  .filter(onlyJpgs())
  .handle(targetDirectory());

И, кстати, обратите внимание на использованиеsetExpectReply. Because integration flows can bebidirectional, этот вызов указывает, что этот конкретный канал является односторонним.

4.6. Активация нашего процесса интеграции

Когда мы добавили все наши компоненты, нам понадобитсяregister our IntegrationFlow as a bean, чтобы активировать его:

@Bean
public IntegrationFlow fileMover() {
    return IntegrationFlows.from(sourceDirectory(), c -> c.poller(Pollers.fixedDelay(10000)))
      .filter(onlyJpgs())
      .handle(targetDirectory())
      .get();
}

Метод get  извлекает экземплярIntegrationFlow, который нам нужно зарегистрировать как Spring Bean.

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

И теперь наше приложение начнет перемещать файлы из исходного каталога в целевой каталог.

5. Дополнительные компоненты

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

Давайте посмотрим на несколько других распространенных компонентов Spring Integration и посмотрим, как мы можем их использовать.

5.1. Каналы сообщений

Как упоминалось ранее,Message Channel - это еще один способ инициализировать поток:

IntegrationFlows.from("anyChannel")

Мы можем прочитать это как «пожалуйста, найдите или создайте компонент канала с именемanyChannel. Затем прочтите все данные, которые поступают вanyChannel из других потоков ».

Но на самом деле это более универсальный, чем это.

Проще говоря, канал отделяет производителей от потребителей, и мы можем думать о нем как оQueueв Java. A channel can be inserted at any point in the flow.

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

@Bean
public PriorityChannel alphabetically() {
    return new PriorityChannel(1000, (left, right) ->
      ((File)left.getPayload()).getName().compareTo(
        ((File)right.getPayload()).getName()));
}

Затем мы можем вставить вызовchannel между нашим потоком:

@Bean
public IntegrationFlow fileMover() {
    return IntegrationFlows.from(sourceDirectory())
      .filter(onlyJpgs())
      .channel("alphabetically")
      .handle(targetDirectory())
      .get();
}

Есть десятки каналов на выбор,some of the more handy ones being for concurrency, auditing, or intermediate persistence (подумайте о буферах Kafka или JMS).

Кроме того, каналы могут быть мощными в сочетании сBridges.

5.2. Мост

Когда мы хотимcombine two channels, мы используемBridge.

Давайте представим, что вместо записи непосредственно в выходной каталог, наше приложение для перемещения файлов записывает в другой канал:

@Bean
public IntegrationFlow fileReader() {
    return IntegrationFlows.from(sourceDirectory())
      .filter(onlyJpgs())
      .channel("holdingTank")
      .get();
}

Теперь, поскольку мы просто записали его в канал,we can bridge from there to other flows.

Давайте создадим мост, который опрашивает наш накопительный резервуар на предмет сообщений и записывает их в пункт назначения:

@Bean
public IntegrationFlow fileWriter() {
    return IntegrationFlows.from("holdingTank")
      .bridge(e -> e.poller(Pollers.fixedRate(1, TimeUnit.SECONDS, 20)))
      .handle(targetDirectory())
      .get();
}

Опять же, поскольку мы писали в промежуточный канал, теперь мы можем добавить еще один потокthat takes these same files and writes them at a different rate:

@Bean
public IntegrationFlow anotherFileWriter() {
    return IntegrationFlows.from("holdingTank")
      .bridge(e -> e.poller(Pollers.fixedRate(2, TimeUnit.SECONDS, 10)))
      .handle(anotherTargetDirectory())
      .get();
}

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

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

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

В этой статье мы увидели различные способы использования Spring DS Integration Java для создания различных конвейеров интеграции.

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

Также мы рассмотрели несколько других компонентов, таких как каналы и мосты.

Полный исходный код, используемый в этом руководстве, доступенover on Github.