Spring WebFlux is de nieuwe module geïntroduceerd in Spring 5. Spring WebFlux is de eerste stap richting het reactieve programmeermodel in het Spring-framework.
Spring Reactief Programmeren
Als u nieuw bent in het reactieve programmeermodel, raad ik u ten zeerste aan de volgende artikelen door te nemen om meer te leren over reactief programmeren.
Als u nieuw bent in Spring 5, bekijk dan alstublieft Spring 5 Functies.
Spring WebFlux
Spring WebFlux is het alternatief voor de Spring MVC-module. Spring WebFlux wordt gebruikt om volledig asynchrone en niet-blokkerende toepassingen te creëren die zijn gebouwd op het event-loop uitvoeringsmodel. De onderstaande diagram uit de officiële Spring-documentatie geeft een goed inzicht in de vergelijking tussen Spring WebFlux en Spring Web MVC. Als u een webtoepassing of REST-webservice wilt ontwikkelen op een niet-blokkerend reactief model, kunt u kijken naar Spring WebFlux. Spring WebFlux wordt ondersteund op Tomcat, Jetty, Servlet 3.1+ containers, evenals op niet-Servlet runtimes zoals Netty en Undertow. Spring WebFlux is gebouwd op Project Reactor. Project Reactor is de implementatie van de Reactive Streams-specificatie. Reactor biedt twee typen:
- Mono: implementeert Publisher en retourneert 0 of 1 elementen
- Flux: implementeert Publisher en retourneert N elementen.
Spring WebFlux Hello World Voorbeeld
Laten we een eenvoudige Spring WebFlux Hello World-toepassing bouwen. We zullen een eenvoudige REST-webdienst maken en Spring Boot gebruiken om deze op de standaard Netty-server uit te voeren. Onze uiteindelijke projectstructuur ziet eruit zoals te zien is in de onderstaande afbeelding. Laten we elk onderdeel van de toepassing stap voor stap bekijken.
Spring WebFlux Maven-afhankelijkheden
<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>
De belangrijkste afhankelijkheden zijn spring-boot-starter-webflux
en spring-boot-starter-parent
. Enkele andere afhankelijkheden zijn bedoeld voor het maken van JUnit-testcases.
Spring WebFlux Handler
De methode van de Spring WebFlux Handler verwerkt het verzoek en retourneert Mono
of Flux
als reactie.
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!"));
}
}
Merk op dat het reactieve component Mono
de body van ServerResponse
bevat. Bekijk ook de functieketen om het retourneertype, de responscode en de body in te stellen.
Spring WebFlux Router
De routermethoden worden gebruikt om routes voor de applicatie te definiëren. Deze methoden retourneren een RouterFunction
-object dat ook de ServerResponse
-body bevat.
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);
}
}
We stellen dus een GET-methode bloot voor /helloWorld
en de clientaanroep moet een platte tekstreactie accepteren.
Spring Boot Applicatie
Laten we onze eenvoudige WebFlux-applicatie configureren met 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);
}
}
Als je naar de bovenstaande code kijkt, zie je niets wat te maken heeft met Spring WebFlux. Maar Spring Boot zal onze applicatie configureren als Spring WebFlux omdat we de afhankelijkheid van de spring-boot-starter-webflux
-module hebben toegevoegd.
Ondersteuning voor Java 9-modules
Onze applicatie is klaar om uitgevoerd te worden op Java 8, maar als je Java 9 gebruikt, moeten we ook de klasse module-info.java
toevoegen.
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;
}
Het uitvoeren van de Spring WebFlux Spring Boot App
Als je Spring-ondersteuning hebt in Eclipse, kun je de bovenstaande klasse uitvoeren als een Spring Boot-applicatie. Als je de opdrachtregel wilt gebruiken, open dan de terminal en voer het commando
mvn spring-boot:run
uit vanuit de projectbronmap. Zodra de app draait, let dan op de volgende logberichten om ervoor te zorgen dat alles goed is met onze app. Het is ook handig als je deze eenvoudige app uitbreidt door meer routes en functionaliteiten toe te voegen.
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)
Uit de logs blijkt duidelijk dat onze app draait op de Netty-server op poort 8080. Laten we doorgaan en onze applicatie testen.
Spring WebFlux App-test
We kunnen onze app testen met verschillende methoden.
-
Gebruik de CURL-opdracht
$ curl https://localhost:8080/helloWorld Hallo wereld! $
-
Start URL in de browser
-
Het gebruik van WebTestClient vanaf Spring 5 Hier is een JUnit-testprogramma om onze Rest-webservice te testen met behulp van
WebTestClient
van Spring 5 reactieve 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") // GET-methode en URI .accept(MediaType.TEXT_PLAIN) // instellen van ACCEPT-Inhoud .exchange() // geeft toegang tot reactie .expectStatus().isOk() // controleren of de reactie OK is .expectBody(String.class).isEqualTo("Hallo wereld!"); // controleren op reactietype en bericht } }
Voer het uit als een JUnit-testgeval en het zou met vlag en wimpel moeten slagen.
-
Het gebruik van WebClient van Spring Web Reactive We kunnen ook
WebClient
gebruiken om de REST-webservice aan te roepen.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("Resultaat = " + result.flatMap(res -> res.bodyToMono(String.class)).block()); } }
Voer het gewoon uit als een eenvoudige Java-toepassing en je zou het juiste resultaat moeten zien met veel debugberichten.
Samenvatting
In deze post hebben we geleerd over Spring WebFlux en hoe je een hallo-wereld reactieve Restful webservice kunt bouwen. Het is goed om te zien dat populaire frameworks zoals Spring de reactieve programmeermodel ondersteunen. Maar we hebben nog veel te bespreken, omdat als al je afhankelijkheden niet reactief en niet-blokkerend zijn, dan is je applicatie ook niet echt reactief. Bijvoorbeeld, relationele databaseleveranciers hebben geen reactieve drivers omdat ze afhankelijk zijn van JDBC, dat niet reactief is. Daarom is de Hibernate API ook niet reactief. Dus als je relationele databases gebruikt, kun je nog geen echt reactieve applicatie bouwen. Ik hoop dat dit eerder dan later zal veranderen.
Je kunt de projectcode downloaden van mijn GitHub Repository.
Referentie: Officiële Documentatie
Source:
https://www.digitalocean.com/community/tutorials/spring-webflux-reactive-programming