Spring DSL Java de integração

Spring DSL Java de integração

1. Introdução

Neste tutorial, aprenderemos sobre o Spring Integration Java DSL para a criação de integrações de aplicativos.

Pegaremos a integração de movimentação de arquivos que criamos emIntroduction to Spring Integratione usaremos o DSL.

2. Dependências

O Spring Integration Java DSL faz parte deSpring Integration Core.

Portanto, podemos adicionar essa dependência:


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

E para trabalhar em nosso aplicativo de movimentação de arquivos, também precisaremos deSpring Integration File:


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

3. Spring DSL Java de integração

Antes do Java DSL, os usuários configuravam os componentes do Spring Integration em XML.

A DSL apresenta alguns construtores fluentes a partir dos quais podemos criar facilmente um pipeline de integração Spring completo puramente em Java.

Então, digamos que queremos criar um canal que coloque em maiúsculas todos os dados que chegam pelo tubo.

No passado, poderíamos ter feito:



E agora podemos fazer:

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

4. O aplicativo de movimentação de arquivos

Para iniciar nossa integração de movimentação de arquivos, precisaremos de alguns blocos de construção simples.

4.1. Fluxo de Integração

O primeiro bloco de construção de que precisamos é um fluxo de integração, que podemos obter doIntegrationFlows builder:

IntegrationFlows.from(...)

from can leva vários tipos, mas neste tutorial, veremos apenas três:

  • MessageSources

  • MessageChannels, e

  • Strings

Falaremos sobre os três em breve.

Depois de chamarfrom, alguns métodos de personalização estão agora disponíveis para nós:

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

Em última análise,IntegrationFlows sempre produzirá uma instância deIntegrationFlow, 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. Descrevendo uma fonte de entrada

Primeiro, para mover os arquivos, precisaremos indicar ao nosso fluxo de integração onde devemos procurá-los, e para isso, precisamos de umMessageSource:

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

Simplificando, aMessageSource é um lugar de ondemessages can come that are external to the application.

Mais especificamente, precisamos de algo que possaadapt essa fonte externa na representação de mensagens Spring. E como esteadaptation está focado eminput, eles são freqüentemente chamados deInput Channel Adapters.

A dependência despring-integration-file  nos dá um adaptador de canal de entrada que é ótimo para nosso caso de uso:FileReadingMessageSource:

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

Aqui, nossoFileReadingMessageSource estará lendo um diretório dado porINPUT_DIRe criará umMessageSource a partir dele.

Vamos especificar isso como nossa fonte em um sinvocationIntegrationFlows.from :

IntegrationFlows.from(sourceDirectory());

4.3. Configurando uma fonte de entrada

Agora, se estamos pensando nisso como um aplicativo de longa duração,we’ll probably want to be able to notice files as they come in, não apenas mova os arquivos que já estão lá na inicialização.

Para facilitar isso,from também pode receberconfigurers extra como personalização adicional da fonte de entrada:

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

Nesse caso, podemos tornar nossa fonte de entrada mais resiliente, dizendo ao Spring Integration para pesquisar essa fonte - nosso sistema de arquivos neste caso - a cada 10 segundos.

E, claro, isso não se aplica apenas a nossa fonte de entrada de arquivo, poderíamos adicionar este poller a qualquerMessageSource.

4.4. Filtrando mensagens de uma fonte de entrada

Em seguida, vamos supor que queremos que nosso aplicativo de movimentação de arquivos mova apenas arquivos específicos, digamos, arquivos de imagem com extensãojpg.

Para isso, podemos usarGenericSelector:

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

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

Então, vamos atualizar nosso fluxo de integração novamente:

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

Ou,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. Tratamento de mensagens com ativadores de serviço

Agora que temos uma lista filtrada de arquivos, precisamos gravá-los em um novo local.

Service Activators are onde nos voltamos quando pensamos sobre saídas na integração Spring.

Vamos usar o ativador de serviçoFileWritingMessageHandler despring-integration-file:

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

Aqui, nossaFileWritingMessageHandler grava a carga útil de cadaMessage que recebe emOUTPUT_DIR.

Mais uma vez, vamos atualizar:

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

E observe, a propósito, o uso desetExpectReply. Because integration flows can bebidirectional, esta invocação indica que este canal específico é unilateral.

4.6. Ativando nosso fluxo de integração

Depois de adicionar todos os nossos componentes, precisamosregister our IntegrationFlow as a bean para ativá-lo:

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

O método get extrai uma instânciaIntegrationFlow que precisamos registrar como Spring Bean.

Assim que nosso contexto de aplicativo é carregado, todos os nossos componentes contidos em nossoIntegrationFlow são ativados.

E agora, nosso aplicativo começará a mover arquivos do diretório de origem para o diretório de destino.

5. Componentes adicionais

Em nosso aplicativo de movimentação de arquivos baseado em DSL, criamos um adaptador de canal de entrada, um filtro de mensagens e um ativador de serviço.

Vamos dar uma olhada em alguns outros componentes comuns do Spring Integration e ver como podemos usá-los.

5.1. Canais de Mensagem

Conforme mencionado anteriormente, aMessage Channel é outra maneira de inicializar um fluxo:

IntegrationFlows.from("anyChannel")

Podemos ler isso como “encontre ou crie um bean de canal chamadoanyChannel. Em seguida, leia todos os dados que são alimentados emanyChannel de outros fluxos. ”

Mas, na verdade, é mais de propósito geral do que isso.

Simplificando, um canal abstrai os produtores dos consumidores e podemos pensar nele como um JavaQueue. A channel can be inserted at any point in the flow.

Digamos, por exemplo, que desejamos priorizar os arquivos conforme eles são movidos de um diretório para o próximo:

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

Então, podemos inserir uma invocação parachannel entre nosso fluxo:

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

Existem dezenas de canais para escolher,some of the more handy ones being for concurrency, auditing, or intermediate persistence (pense em buffers Kafka ou JMS).

Além disso, os canais podem ser poderosos quando combinados comBridges.

5.2. Ponte

Quando queremoscombine two channels, usamos umBridge.

Vamos imaginar que, em vez de gravar diretamente em um diretório de saída, fizemos com que nosso aplicativo de movimentação de arquivos fosse gravado em outro canal:

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

Agora, como simplesmente escrevemos em um canal,we can bridge from there to other flows.

Vamos criar uma ponte que pesquisa nosso tanque de retenção para mensagens e as grava em um destino:

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

Novamente, como escrevemos em um canal intermediário, agora podemos adicionar outro fluxothat 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();
}

Como podemos ver, pontes individuais podem controlar a configuração de polling para diferentes manipuladores.

Assim que nosso contexto de aplicativo é carregado, agora temos um aplicativo mais complexo em ação que começará a mover arquivos do diretório de origem para dois diretórios de destino.

6. Conclusão

Neste artigo, vimos várias maneiras de usar o DSL Java do Spring Integration para criar diferentes pipelines de integração.

Basicamente, conseguimos recriar o aplicativo de movimentação de arquivos de um tutorial anterior, desta vez usando java puro.

Além disso, vimos alguns outros componentes, como canais e pontes.

O código-fonte completo usado neste tutorial está disponívelover on Github.