הַ- Spring WebFlux הִוא הַמוֹדוּל הַחָדָשׁ שֶׁהוּכְנִיסוּ בְּ- Spring 5. הַ- Spring WebFlux הִיא הַצָּעָד הָרִאשׁוֹן לְעַבוֹדָה רְאַקְטִיבִית בְּמוֹדֶל תְּכֵנָה שֶׁל Spring.
תְּכֵנָה רְאַקְטִיבִית בְּ- Spring
אִם אַתָּה חָדָשׁ לְמוֹדֶל תְּכֵנָה רְאַקְטִיבִית, אָז אָנִי מְמַלֵּיץ מְאוֹד לָעֲבוֹר עַל הֲמַלְצוֹת הַבְּאֵים כְּדֵי לִלְמוֹד עַל תְּכֵנוֹת רְאַקְטִיבִיּוֹת.
אִם אַתָּה חָדָשׁ לְ- Spring 5, בְּבַקָּשָׁה עֲבוֹר עַל
תְּכוּנוֹת Spring 5.
Spring WebFlux
Spring WebFlux היא האלטרנטיבה למודול Spring MVC. Spring WebFlux משמשת ליצירת יישומים לגמרי אסינכרוניים ולא חוסמים שנבנו על דגם הביצוע של לולאות אירועים. התרשים למטה מתיעוד הרשמי של Spring מספק מבט מעולה על השוואה בין Spring WebFlux ל Spring Web MVC. אם אתה מחפש לפתח אפליקציה אינטרנטית או שירות רשת Rest על דגם ריאקטיבי לא חוסם, תוכל לבחון את Spring WebFlux. Spring WebFlux נתמך על ידי Tomcat, Jetty, תכונות Servlet 3.1+ וגם על סביבות לא-Servlet כמו Netty ו- Undertow. Spring WebFlux מבוססת על Project Reactor. Project Reactor הוא יישום של תקן Reactive Streams. Reactor מספק שני סוגים:
- Mono: מיישם Publisher ומחזיר 0 או 1 אלמנטים
- Flux: מיישם Publisher ומחזיר N אלמנטים.
דוגמה לשלום עולם ב-Spring WebFlux
בואו נבנה יישום פשוט של Spring WebFlux Hello World. ניצור שירות REST פשוט ונשתמש ב-Spring Boot כדי להפעיל אותו על שרת Netty בברירת המחדל. מבנה הפרויקט הסופי שלנו נראה כמו בתמונה למטה. בואו נבחן כל רכיב של היישום אחד אחד.
תלויות Maven של 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>
התלויות החשובות ביותר הן spring-boot-starter-webflux
ו־spring-boot-starter-parent
. תלויות אחרות משמשות ליצירת מבחני JUnit.
טפסן של Spring WebFlux
שיטת הטפסן של Spring WebFlux מטפלת בבקשה ומחזירה Mono
או Flux
כתגובה.
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!"));
}
}
שימו לב שרכיב הריאקטיבי Mono
מחזיק את גוף ה־ServerResponse
. כן גם תבחינו בשרשור הפונקציות להגדרת סוג התוכן שמוחזר, קוד התגובה והגוף.
ראוטר של Spring WebFlux
השיטות של נתב משמשות להגדרת המסלולים לאפליקציה. השיטות האלו מחזירות אובייקט RouterFunction
שגם מחזיק בגוף ServerResponse
. כך שאנחנו מחשפים שיטת GET לכתובת /helloWorld
וקריאת הלקוח אמורה לקבל תגובת טקסט פשוטה.
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);
}
}
יישום Spring Boot
בואו נגדיר את האפליקציה שלנו ב- WebFlux עם 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);
}
}
אם תביטו בקוד למעלה, אין שום קשר ל- Spring WebFlux. אבל Spring Boot יגדיר את האפליקציה שלנו כ- Spring WebFlux מאחר והוספנו את תלות המודול spring-boot-starter-webflux
.
תמיכה במודולים של Java 9
האפליקציה שלנו מוכנה להרצה על Java 8, אך אם אתה משתמש ב-Java 9 אנו צריכים להוסיף גם את המחלקה 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;
}
הרצת אפליקציה של Spring WebFlux ב-Spring Boot
אם יש לך תמיכה ב־Spring בְּEclipse, אז תוכל להריץ את המחלקה למעלה כיישום Spring Boot. אם אתה רוצה להשתמש בשורת פקודה, אז תפתח טרמינל ותפעיל את הפקודה
mvn spring-boot:run
מתיקיית מקור הפרויקט. פעם שהיישום פועל, שים לב להודעות הלוג הבאות כדי לוודא שהכל בסדר עם היישום שלנו. זה גם יעזור כאשר תרחיב את היישום הפשוט הזה על ידי הוספת יותר מסלולים ופונקציות.
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)
מובן מהלוגים שהיישום שלנו פועל על שרת Netty בפורט 8080. בוא נמשיך ונבדוק את היישום שלנו.
בדיקת היישום של Spring WebFlux
אנחנו יכולים לבדוק את היישום שלנו באמצעות שיטות שונות.
-
שימוש בפקודת CURL
$ curl https://localhost:8080/helloWorld Hello World! $
-
פתיחת כתובת URL בדפדפן
-
שימוש ב-WebTestClient מ־Spring 5 זהו תוכנית מבחן JUnit לבדיקת השירות האינטרנט שלנו באמצעות
WebTestClient
מתוך Spring 5 ריאקטיבי.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 method and URI .accept(MediaType.TEXT_PLAIN) //setting ACCEPT-Content .exchange() //gives access to response .expectStatus().isOk() //checking if response is OK .expectBody(String.class).isEqualTo("Hello World!"); // checking for response type and message } }
הרץ אותה כמבחן JUnit והוא צריך לעבור בצבעים זוהרים.
-
שימוש ב-WebClient מ-Spring Web Reactive אנו יכולים גם להשתמש ב-
WebClient
כדי לקרוא לשירות האינטרנט התואם כתובת 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()); } }
רק הריצו אותו כיישום ג'אווה פשוט ואתם צריכים לראות את הפלט המתאים עם הודעות דיבוג רבות.
סיכום
בפוסט זה למדנו על Spring WebFlux וכיצד לבנות שירות רשת Restful ריאקטיבי "hello world". זה מרגש לראות שגם ספריות פופולריות כמו Spring תומכות במודל תכנות ריאקטיבי. אך יש לנו הרבה לכסות מכיוון שאם כל התלויותיך אינן ריאקטיביות ובלתי חוסמות, אז היישום שלך אינו רקטיבי באמת. לדוגמה, ספקי מסדי נתונים יחסיים אינם מספקים דרייברים ריאקטיביים מאחר והם תלויים ב-JDBC, שאינו ריאקטיבי. לכן, גם ממשק ה-Hibernate הוא אינו ריאקטיבי. לכן, אם אתה משתמש במסדי נתונים יחסיים, אתה לא יכול לבנות אפליקציה באמת ריאקטיבית, לפחות כרגע. אני מקווה שזה ישתנה מהר מאוד.
ניתן להוריד את קוד הפרויקט מה מאגר הקוד שלי ב-GitHub.
התייחסות: תיעוד רשמי
Source:
https://www.digitalocean.com/community/tutorials/spring-webflux-reactive-programming