Spring WebFlux ist das neue Modul, das in Spring 5 eingeführt wurde. Spring WebFlux ist der erste Schritt hin zum reaktiven Programmiermodell im Spring-Framework.
Reaktive Programmierung in Spring
Wenn Sie neu im reaktiven Programmiermodell sind, empfehle ich Ihnen dringend, die folgenden Artikel zur Erlernung der reaktiven Programmierung zu lesen.
Wenn Sie neu in Spring 5 sind, lesen Sie bitte Spring 5 Funktionen.
Spring WebFlux
Spring WebFlux ist das Gegenstück zum Modul Spring MVC. Spring WebFlux wird verwendet, um vollständig asynchrone und nicht-blockierende Anwendungen auf Basis des Ereignisschleifen-Ausführungsmodells zu erstellen. Die folgende Diagramm aus der offiziellen Spring-Dokumentation bietet einen guten Einblick in den Vergleich von Spring WebFlux mit Spring Web MVC. Wenn Sie eine Webanwendung oder einen REST-Webservice auf dem nicht-blockierenden reaktiven Modell entwickeln möchten, können Sie sich Spring WebFlux ansehen. Spring WebFlux wird auf Tomcat, Jetty, Servlet 3.1+ Containern sowie auf nicht-Servlet-Laufzeiten wie Netty und Undertow unterstützt. Spring WebFlux basiert auf Project Reactor. Project Reactor ist die Implementierung der Reactive Streams-Spezifikation. Reactor bietet zwei Typen:
- Mono: implementiert Publisher und liefert 0 oder 1 Elemente
- Flux: implementiert Publisher und liefert N Elemente.
Spring WebFlux Hallo Welt Beispiel
Lass uns eine einfache Spring WebFlux Hello World-Anwendung erstellen. Wir werden einen einfachen REST-Webdienst erstellen und Spring Boot verwenden, um ihn auf dem Standard-Netty-Server auszuführen. Unsere endgültige Projektstruktur sieht wie im folgenden Bild aus. Lass uns jeden Bestandteil der Anwendung einzeln betrachten.
Spring WebFlux Maven-Abhängigkeiten
<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>
Die wichtigsten Abhängigkeiten sind spring-boot-starter-webflux
und spring-boot-starter-parent
. Einige andere Abhängigkeiten dienen der Erstellung von JUnit-Testfällen.
Spring WebFlux Handler
Die Handler-Methode von Spring WebFlux verarbeitet die Anfrage und gibt Mono
oder Flux
als Antwort zurück.
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!"));
}
}
Beachte, dass das reaktive Element Mono
den Body von ServerResponse
enthält. Beachte auch die Funktionskette zur Festlegung des Rückgabetyps, des Antwortcodes und des Body.
Spring WebFlux Router
Router-Methoden werden verwendet, um Routen für die Anwendung zu definieren. Diese Methoden geben ein RouterFunction
-Objekt zurück, das auch den ServerResponse
-Body enthält.
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);
}
}
Wir stellen also eine GET-Methode für /helloWorld
bereit, und der Clientaufruf sollte eine Antwort als reinen Text akzeptieren.
Spring Boot-Anwendung
Konfigurieren wir unsere einfache WebFlux-Anwendung mit 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);
}
}
Wenn Sie sich den obigen Code ansehen, gibt es nichts, was mit Spring WebFlux zu tun hat. Aber Spring Boot wird unsere Anwendung als Spring WebFlux konfigurieren, da wir die Abhängigkeit des spring-boot-starter-webflux
-Moduls hinzugefügt haben.
Java 9 Module-Unterstützung
Unsere Anwendung ist bereit, auf Java 8 ausgeführt zu werden, aber wenn Sie Java 9 verwenden, müssen wir auch die Klasse module-info.java
hinzufügen.
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;
}
Starten der Spring WebFlux Spring Boot-App
Wenn Sie in Eclipse Spring-Unterstützung haben, können Sie die obige Klasse als Spring Boot App ausführen. Wenn Sie die Befehlszeile verwenden möchten, öffnen Sie das Terminal und führen Sie den Befehl
mvn spring-boot:run
im Quellverzeichnis des Projekts aus. Sobald die App läuft, beachten Sie die folgenden Protokollmeldungen, um sicherzustellen, dass alles gut mit unserer App ist. Dies ist auch hilfreich, wenn Sie diese einfache App erweitern, indem Sie weitere Routen und Funktionen hinzufügen.
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)
Aus den Protokollen geht hervor, dass unsere App auf dem Netty-Server auf Port 8080 läuft. Gehen wir nun voran und testen unsere Anwendung.
Spring WebFlux App Test
Wir können unsere App mit verschiedenen Methoden testen.
-
Verwendung des CURL-Befehls
$ curl https://localhost:8080/helloWorld Hallo Welt! $
-
URL im Browser öffnen
-
Verwendung von WebTestClient aus Spring 5 Hier ist ein JUnit-Testprogramm, um unseren Rest-Webdienst mit
WebTestClient
aus Spring 5 Reactive Web zu testen.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") // GET-Methode und URI .accept(MediaType.TEXT_PLAIN) // Einstellen von ACCEPT-Content .exchange() // gibt Zugriff auf die Antwort .expectStatus().isOk() // Überprüfen, ob die Antwort OK ist .expectBody(String.class).isEqualTo("Hallo Welt!"); // Überprüfen des Antworttyps und der Nachricht } }
Führen Sie es als JUnit-Testfall aus, und es sollte mit fliegenden Farben bestehen.
-
Verwendung von WebClient aus Spring Web Reactive Wir können auch
WebClient
verwenden, um den REST-Webservice aufzurufen.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()); } }
Führen Sie es einfach als einfache Java-Anwendung aus, und Sie sollten die richtige Ausgabe mit vielen Debug-Nachrichten sehen.
Zusammenfassung
In diesem Beitrag haben wir über Spring WebFlux und den Aufbau eines Hallo-Welt-Reactive-Restful-Webservices gelernt. Es ist gut zu sehen, dass beliebte Frameworks wie Spring das reaktive Programmiermodell unterstützen. Aber wir haben noch viel zu behandeln, denn wenn nicht alle Ihre Abhängigkeiten reaktiv und nicht blockierend sind, ist Ihre Anwendung auch nicht wirklich reaktiv. Zum Beispiel haben relationale Datenbankanbieter keine reaktiven Treiber, weil sie von JDBC abhängen, das nicht reaktiv ist. Daher ist auch die Hibernate-API nicht reaktiv. Wenn Sie also relationale Datenbanken verwenden, können Sie noch keine wirklich reaktive Anwendung erstellen. Ich bin zuversichtlich, dass sich das früher als später ändern wird.
Sie können den Projektcode aus meinem GitHub-Repository herunterladen.
Referenz: Offizielle Dokumentation
Source:
https://www.digitalocean.com/community/tutorials/spring-webflux-reactive-programming