Spring WebFlux est le nouveau module introduit dans Spring 5. Spring WebFlux est la première étape vers le modèle de programmation réactive dans le framework Spring.
Programmation réactive Spring
Si vous êtes nouveau dans le modèle de programmation réactive, je vous recommande vivement de consulter les articles suivants pour en apprendre davantage sur la programmation réactive.
Si vous débutez avec Spring 5, veuillez consulter les fonctionnalités de Spring 5.
Spring WebFlux
Spring WebFlux est l’alternative au module Spring MVC. Spring WebFlux est utilisé pour créer des applications entièrement asynchrones et non bloquantes, basées sur le modèle d’exécution en boucle d’événements. Le diagramme ci-dessous, extrait de la documentation officielle de Spring, donne un aperçu de la comparaison entre Spring WebFlux et Spring Web MVC. Si vous souhaitez développer une application web ou un service web REST basé sur un modèle réactif non bloquant, vous pouvez vous tourner vers Spring WebFlux. Spring WebFlux est pris en charge par Tomcat, Jetty, les conteneurs Servlet 3.1+ ainsi que par des exécutions non Servlet telles que Netty et Undertow. Spring WebFlux est construit sur le projet Reactor. Project Reactor est l’implémentation de la spécification Reactive Streams. Reactor fournit deux types :
- Mono : implémente Publisher et renvoie 0 ou 1 élément
- Flux : implémente Publisher et renvoie N éléments.
Exemple de « Hello World » avec Spring WebFlux
Construisons une application Hello World simple avec Spring WebFlux. Nous allons créer un service web REST simple et utiliser Spring Boot pour l’exécuter sur le serveur Netty par défaut. La structure finale de notre projet ressemble à l’image ci-dessous. Examinons chaque composant de l’application un par un.
Dépendances Maven de Spring WebFlux
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>SpringWebflux</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring WebFlux</name>
<description>Spring WebFlux Example</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<jdk.version>1.9</jdk.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Les dépendances les plus importantes sont spring-boot-starter-webflux
et spring-boot-starter-parent
. D’autres dépendances servent à créer des cas de test JUnit.
Handler de Spring WebFlux
La méthode Handler de Spring WebFlux gère la requête et renvoie un Mono
ou un Flux
en réponse.
package com.journaldev.spring.component;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class HelloWorldHandler {
public Mono<ServerResponse> helloWorld(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("Hello World!"));
}
}
Remarquez que le composant réactif Mono
contient le corps de la réponse ServerResponse
. Regardez également la chaîne de fonctions pour définir le type de contenu de retour, le code de réponse et le corps.
Routeur de Spring WebFlux
Les méthodes de routeur sont utilisées pour définir des itinéraires pour l’application. Ces méthodes renvoient un objet RouterFunction
qui contient également le corps de la réponse du serveur.
package com.journaldev.spring.component;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
@Configuration
public class HelloWorldRouter {
@Bean
public RouterFunction<ServerResponse> routeHelloWorld(HelloWorldHandler helloWorldHandler) {
return RouterFunctions.route(RequestPredicates.GET("/helloWorld")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), helloWorldHandler::helloWorld);
}
}
Ainsi, nous exposons une méthode GET pour /helloWorld
et l’appel du client devrait accepter une réponse en texte brut.
Application Spring Boot
Configurons maintenant notre simple application WebFlux avec Spring Boot.
package com.journaldev.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Si vous regardez le code ci-dessus, il n’y a rien lié à Spring WebFlux. Cependant, Spring Boot configurera notre application en tant que Spring WebFlux, car nous avons ajouté la dépendance du module spring-boot-starter-webflux
.
Prise en charge des modules Java 9
Notre application est prête à être exécutée sur Java 8, mais si vous utilisez Java 9, nous devons également ajouter la classe module-info.java
.
module com.journaldev.spring {
requires reactor.core;
requires spring.web;
requires spring.beans;
requires spring.context;
requires spring.webflux;
requires spring.boot;
requires spring.boot.autoconfigure;
exports com.journaldev.spring;
}
Exécution de l’application Spring WebFlux Spring Boot
Si vous avez le support de Spring dans Eclipse, vous pouvez exécuter la classe ci-dessus en tant qu’application Spring Boot. Si vous préférez utiliser la ligne de commande, ouvrez le terminal et exécutez la commande
mvn spring-boot:run
depuis le répertoire source du projet. Une fois l’application en cours d’exécution, observez les messages journaux suivants pour vous assurer que tout va bien avec notre application. Cela est également utile lorsque vous étendez cette application simple en ajoutant plus de routes et de fonctionnalités.
2018-05-07 15:01:47.893 INFO 25158 --- [ main] o.s.w.r.f.s.s.RouterFunctionMapping : Mapped ((GET && /helloWorld) && Accept: [text/plain]) -> com.journaldev.spring.component.HelloWorldRouter$$Lambda$501/704766954@6eeb5d56
2018-05-07 15:01:48.495 INFO 25158 --- [ctor-http-nio-1] r.ipc.netty.tcp.BlockingNettyContext : Started HttpServer on /0:0:0:0:0:0:0:0:8080
2018-05-07 15:01:48.495 INFO 25158 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080
2018-05-07 15:01:48.501 INFO 25158 --- [ main] com.journaldev.spring.Application : Started Application in 1.86 seconds (JVM running for 5.542)
Il est clair à partir des journaux que notre application s’exécute sur le serveur Netty sur le port 8080. Allons-y et testons notre application.
Test de l’application Spring WebFlux
Nous pouvons tester notre application avec différentes méthodes.
-
Utilisation de la commande CURL
$ curl https://localhost:8080/helloWorld Hello World! $
-
Lancer l’URL dans le navigateur
-
À l’aide de WebTestClient de Spring 5 Voici un programme de test JUnit pour tester notre service web Rest en utilisant
WebTestClient
de Spring 5 reactive web.package com.journaldev.spring; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class SpringWebFluxTest { @Autowired private WebTestClient webTestClient; @Test public void testHelloWorld() { webTestClient .get().uri("/helloWorld") // Méthode GET et URI .accept(MediaType.TEXT_PLAIN) // Définition de ACCEPT-Content .exchange() // Donne accès à la réponse .expectStatus().isOk() // Vérifie si la réponse est OK .expectBody(String.class).isEqualTo("Hello World!"); // Vérifie le type de réponse et le message } }
Exécutez-le en tant que cas de test JUnit et il devrait passer avec succès.
-
En utilisant WebClient de Spring Web Reactive, nous pouvons également utiliser
WebClient
pour appeler le service web REST.package com.journaldev.spring.client; import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; public class HelloWorldWebClient { public static void main(String args[]) { WebClient client = WebClient.create("https://localhost:8080"); Mono<ClientResponse> result = client.get() .uri("/helloWorld") .accept(MediaType.TEXT_PLAIN) .exchange(); System.out.println("Résultat = " + result.flatMap(res -> res.bodyToMono(String.class)).block()); } }
Il suffit de l’exécuter comme une simple application java et vous devriez voir la sortie correcte avec beaucoup de messages de débogage.
Résumé
Dans ce post, nous avons appris à propos de Spring WebFlux et comment construire un service web réactif « hello world ». Il est encourageant de voir que des frameworks populaires tels que Spring soutiennent le modèle de programmation réactive. Cependant, nous avons beaucoup à couvrir, car si toutes vos dépendances ne sont pas réactives et non bloquantes, votre application n’est pas vraiment réactive. Par exemple, les fournisseurs de bases de données relationnelles n’ont pas de pilotes réactifs car ils dépendent de JDBC, qui n’est pas réactif. Ainsi, l’API Hibernate n’est également pas réactive. Donc, si vous utilisez des bases de données relationnelles, vous ne pouvez pas construire une application vraiment réactive pour le moment. J’espère que cela changera tôt ou tard.
Vous pouvez télécharger le code du projet depuis mon Dépôt GitHub.
Référence: Documentation Officielle
Source:
https://www.digitalocean.com/community/tutorials/spring-webflux-reactive-programming