Spring WebFlux è il nuovo modulo introdotto in Spring 5. Spring WebFlux è il primo passo verso il modello di programmazione reattiva nel framework Spring.
Programmazione reattiva Spring
Se sei nuovo al modello di programmazione reattiva, ti consiglierei vivamente di leggere i seguenti articoli per apprendere la programmazione reattiva.
Se sei nuovo a Spring 5, per favore leggi Caratteristiche di Spring 5.
Spring WebFlux
Spring WebFlux è l’alternativa al modulo Spring MVC. Spring WebFlux viene utilizzato per creare applicazioni completamente asincrone e non bloccanti basate sul modello di esecuzione event-loop. Il diagramma seguente, tratto dalla documentazione ufficiale di Spring, fornisce una grande panoramica sul confronto tra Spring WebFlux e Spring Web MVC. Se stai cercando di sviluppare un’applicazione web o un servizio web Rest basato su un modello reattivo non bloccante, puoi considerare Spring WebFlux. Spring WebFlux è supportato su Tomcat, Jetty, container Servlet 3.1+ e su runtime non Servlet come Netty e Undertow. Spring WebFlux è costruito su Project Reactor. Project Reactor è l’implementazione della specifica Reactive Streams. Reactor fornisce due tipi:
- Mono: implementa Publisher e restituisce 0 o 1 elementi
- Flux: implementa Publisher e restituisce N elementi.
Esempio di Hello World di Spring WebFlux
Costruiamo un’applicazione Spring WebFlux Hello World semplice. Creeremo un servizio web REST e utilizzeremo Spring Boot per eseguirlo su un server Netty predefinito. La struttura finale del nostro progetto appare come nell’immagine sottostante. Esaminiamo ogni componente dell’applicazione uno per uno.
Spring WebFlux Maven Dependencies
<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>
Le dipendenze più importanti sono spring-boot-starter-webflux
e spring-boot-starter-parent
. Alcune altre dipendenze sono per la creazione di casi di test JUnit.
Handler di Spring WebFlux
Il metodo dell’Handler di Spring WebFlux gestisce la richiesta e restituisce Mono
o Flux
come risposta.
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!"));
}
}
Nota che il componente reattivo Mono
contiene il corpo di ServerResponse
. Osserva anche la catena di funzioni per impostare il tipo di contenuto di ritorno, il codice di risposta e il corpo.
Router di Spring WebFlux
I metodi del router vengono utilizzati per definire le rotte dell’applicazione. Questi metodi restituiscono un oggetto RouterFunction
che contiene anche il corpo di ServerResponse
.
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);
}
}
Quindi stiamo esponendo un metodo GET per /helloWorld
e la chiamata del client dovrebbe accettare una risposta in testo normale.
Applicazione Spring Boot
Configuriamo la nostra semplice applicazione WebFlux con 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);
}
}
Se guardi il codice sopra, non c’è nulla relativo a Spring WebFlux. Ma Spring Boot configurerà la nostra applicazione come Spring WebFlux poiché abbiamo aggiunto la dipendenza del modulo spring-boot-starter-webflux
.
Supporto dei Moduli Java 9
La nostra applicazione è pronta per essere eseguita su Java 8, ma se stai usando Java 9 allora dobbiamo anche aggiungere 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;
}
Esecuzione dell’App Spring WebFlux Spring Boot
Se hai il supporto di Spring in Eclipse, puoi eseguire la classe sopra come Spring Boot App. Se preferisci utilizzare la riga di comando, apri il terminale e esegui il comando
mvn spring-boot:run
dalla directory sorgente del progetto. Una volta che l’app è in esecuzione, osserva i seguenti messaggi di log per assicurarti che tutto sia a posto con la nostra app. È anche utile quando espandi questa semplice app aggiungendo più percorsi e funzionalità.
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)
Dai log è chiaro che la nostra app sta girando su un server Netty sulla porta 8080. Andiamo avanti e testiamo la nostra applicazione.
Test dell’app Spring WebFlux
Possiamo testare la nostra app con vari metodi.
-
Usando il comando CURL
$ curl https://localhost:8080/helloWorld Hello World! $
-
Lancia l’URL nel Browser
-
Utilizzando WebTestClient da Spring 5 Ecco un programma di test JUnit per testare il nostro servizio web Rest utilizzando
WebTestClient
da Spring 5 web reattivo.pacchetto 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") // metodo GET e URI .accept(MediaType.TEXT_PLAIN) // impostazione ACCEPT-Content .exchange() // fornisce accesso alla risposta .expectStatus().isOk() // verifica se la risposta è OK .expectBody(String.class).isEqualTo("Ciao Mondo!"); // verifica il tipo di risposta e il messaggio } }
Eseguilo come caso di test JUnit e dovrebbe superare con successo.
-
Utilizzando WebClient da Spring Web Reactive Possiamo anche utilizzare
WebClient
per chiamare il servizio 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("Result = " + result.flatMap(res -> res.bodyToMono(String.class)).block()); } }
Esegui semplicemente come un’applicazione java e dovresti vedere l’output corretto con molti messaggi di debug.
Sommario
In questo post abbiamo imparato su Spring WebFlux e come costruire un servizio web reattivo di hello world Restful. È bello vedere che framework popolari come Spring sostengono il modello di programmazione reattiva. Ma abbiamo molto da coprire perché se tutte le tue dipendenze non sono reattive e non bloccanti, allora la tua applicazione non è veramente reattiva. Ad esempio, i fornitori di database relazionali non hanno driver reattivi perché dipendono da JDBC, che non è reattivo. Pertanto, l’API di Hibernate non è reattiva. Quindi se stai usando database relazionali, non puoi costruire un’applicazione veramente reattiva, ancora. Sono fiducioso che cambierà prima che poi.
Puoi scaricare il codice del progetto dal mio Repository GitHub.
Riferimento: Documentazione Ufficiale
Source:
https://www.digitalocean.com/community/tutorials/spring-webflux-reactive-programming