Spring Integration Java DSL

Intégration Spring DSL Java

1. introduction

Dans ce didacticiel, nous allons découvrir le DSL Java Spring Integration pour créer des intégrations d'applications.

Nous allons prendre l’intégration de transfert de fichiers que nous avons intégrée dansIntroduction to Spring Integration et utiliser le DSL à la place.

2. Les dépendances

Le DSL Java Spring Integration fait partie deSpring Integration Core.

Donc, nous pouvons ajouter cette dépendance:


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

Et pour travailler sur notre application de transfert de fichiers, nous aurons également besoin deSpring Integration File:


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

3. Intégration Spring DSL Java

Avant Java DSL, les utilisateurs devaient configurer les composants d'intégration Spring en XML.

Le DSL introduit des générateurs fluides à partir desquels nous pouvons facilement créer un pipeline d'intégration Spring complet uniquement en Java.

Supposons donc que nous voulions créer un canal qui majuscule toutes les données qui transitent par le tuyau.

Dans le passé, nous aurions pu faire:



Et maintenant nous pouvons faire à la place:

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

4. L'application de déplacement de fichiers

Pour commencer notre intégration de déplacement de fichiers, nous aurons besoin de quelques éléments de base simples.

4.1. Flux d'intégration

Le premier bloc de construction dont nous avons besoin est un flux d'intégration, que nous pouvons obtenir du constructeurIntegrationFlows :

IntegrationFlows.from(...)

from can prend plusieurs types, mais dans ce didacticiel, nous n'en examinerons que trois:

  • MessageSources

  • MessageChannels, et

  • Strings

Nous parlerons des trois sous peu.

Après avoir appeléfrom, certaines méthodes de personnalisation nous sont désormais disponibles:

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

En fin de compte,IntegrationFlows produira toujours une instance 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. Décrire une source d'entrée

Tout d'abord, pour déplacer des fichiers, nous devons indiquer à notre flux d'intégration où il doit les rechercher, et pour cela, nous avons besoin d'unMessageSource:

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

En termes simples, unMessageSource est un endroit à partir duquelmessages can come that are external to the application.

Plus précisément, nous avons besoin de quelque chose qui puisseadapt cette source externe dans la représentation de messagerie Spring. Et comme ceadaptation est concentré surinput, on les appelle souventInput Channel Adapters.

La dépendancespring-integration-file  nous donne un adaptateur de canal d'entrée idéal pour notre cas d'utilisation:FileReadingMessageSource:

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

Ici, notreFileReadingMessageSource era en train de lire un répertoire donné parINPUT_DIR et créera unMessageSource à partir de celui-ci.

Spécifions ceci comme notre source dans une sinvocationIntegrationFlows.from :

IntegrationFlows.from(sourceDirectory());

4.3. Configuration d'une source d'entrée

Maintenant, si nous considérons cela comme une application à longue durée de vie,we’ll probably want to be able to notice files as they come in, ne déplacez pas simplement les fichiers qui sont déjà là au démarrage.

Pour faciliter cela,from peut également prendre desconfigurers supplémentaires pour personnaliser davantage la source d'entrée:

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

Dans ce cas, nous pouvons rendre notre source d'entrée plus résiliente en ordonnant à Spring Integration d'interroger cette source (notre système de fichiers dans ce cas) toutes les 10 secondes.

Et, bien sûr, cela ne s'applique pas uniquement à notre source d'entrée de fichier, nous pourrions ajouter ce poller à n'importe quelMessageSource.

4.4. Filtrage des messages à partir d'une source d'entrée

Ensuite, supposons que nous souhaitons que notre application de déplacement de fichiers déplace uniquement des fichiers spécifiques, par exemple des fichiers image ayant l'extensionjpg.

Pour cela, nous pouvons utiliserGenericSelector:

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

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

Alors, mettons à jour à nouveau notre flux d'intégration:

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. Gestion des messages avec les activateurs de service

Maintenant que nous avons une liste filtrée de fichiers, nous devons les écrire dans un nouvel emplacement.

Service Activators  sont ce vers quoi nous nous tournons lorsque nous pensons aux sorties dans Spring Integration.

Utilisons l'activateur de serviceFileWritingMessageHandler despring-integration-file:

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

Ici, notreFileWritingMessageHandler will écrit chaque charge utile deMessage qu'il reçoit dansOUTPUT_DIR.

Encore une fois, mettons à jour:

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

Et notez, en passant, l'utilisation desetExpectReply. Because integration flows can bebidirectional, cet appel indique que ce canal particulier est à sens unique.

4.6. Activation de notre flux d'intégration

Lorsque nous avons ajouté tous nos composants, nous devonsregister our IntegrationFlow as a bean pour l'activer:

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

La méthode get extrait une instanceIntegrationFlow que nous devons enregistrer en tant que Spring Bean.

Dès que notre contexte d'application se charge, tous nos composants contenus dans nosIntegrationFlow sont activés.

Et maintenant, notre application commencera à déplacer les fichiers du répertoire source au répertoire cible.

5. Composants supplémentaires

Dans notre application de transfert de fichiers basée sur DSL, nous avons créé un adaptateur de canal entrant, un filtre de message et un activateur de service.

Examinons quelques autres composants Spring Integration courants et voyons comment nous pourrions les utiliser.

5.1. Canaux de message

Comme mentionné précédemment, unMessage Channel est une autre façon d'initialiser un flux:

IntegrationFlows.from("anyChannel")

Nous pouvons lire ceci comme «veuillez trouver ou créer un bean de canal appeléanyChannel. Ensuite, lisez toutes les données introduites dansanyChannel à partir d'autres flux. »

Mais, en réalité, il est plus polyvalent que cela.

En termes simples, un canal éloigne les producteurs des consommateurs, et nous pouvons le considérer comme un JavaQueue. A channel can be inserted at any point in the flow.

Disons, par exemple, que nous souhaitons hiérarchiser les fichiers lorsqu'ils sont déplacés d'un répertoire à l'autre:

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

Ensuite, nous pouvons insérer un appel àchannel entre notre flux:

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

Il existe des dizaines de canaux parmi lesquels choisir,some of the more handy ones being for concurrency, auditing, or intermediate persistence (pensez aux tampons Kafka ou JMS).

De plus, les canaux peuvent être puissants lorsqu'ils sont combinés avecBridges.

5.2. Pont

Quand on veutcombine two channels, on utilise unBridge.

Imaginons qu'au lieu d'écrire directement dans un répertoire de sortie, nous avons plutôt demandé à notre application de déplacement de fichiers d'écrire sur un autre canal:

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

Maintenant, parce que nous l'avons simplement écrit sur un canal,we can bridge from there to other flows.

Créons un pont qui interroge notre réservoir de stockage pour les messages et les écrit vers une destination:

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

Encore une fois, parce que nous avons écrit sur un canal intermédiaire, nous pouvons maintenant ajouter un autre fluxthat 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();
}

Comme nous pouvons le constater, des ponts individuels peuvent contrôler la configuration d'interrogation pour différents gestionnaires.

Dès que notre contexte d'application est chargé, nous avons maintenant une application plus complexe en action qui commencera à déplacer des fichiers du répertoire source vers deux répertoires cibles.

6. Conclusion

Dans cet article, nous avons vu différentes façons d'utiliser Spring DSL Java Integration pour créer différents pipelines d'intégration.

Pour l’essentiel, nous avons pu recréer l’application de déplacement de fichiers à partir d’un tutoriel précédent, cette fois-ci en java pur.

Nous avons également examiné quelques autres composants tels que les canaux et les ponts.

Le code source complet utilisé dans ce tutoriel est disponibleover on Github.