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 Hello World
ترجمة النص إلى اللغة العربية:
لنقم ببناء تطبيق بسيط “Hello World” باستخدام Spring WebFlux. سنقوم بإنشاء خدمة ويب بسيطة واستخدام 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
أساليب Router تُستخدم لتعريف المسارات لتطبيق. تعيد هذه الأساليب كائن `RouterFunction` الذي يحتوي أيضًا على جسم `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);
}
}
لذا نحن نكشف عن طريقة GET لـ /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()); } }
قم بتشغيله كتطبيق Java بسيط ويجب أن ترى الناتج الصحيح مع الكثير من رسائل التصحيح.
ملخص
في هذه المقالة تعلمنا عن Spring WebFlux وكيفية بناء خدمة ويب ردود فعلية بنموذج Restful. من الجيد رؤية أن إطارات العمل الشهيرة مثل Spring تدعم نموذج البرمجة الردائي. ولكن لدينا الكثير لنغطيه لأنه إذا لم تكن جميع التبعيات الخاصة بك ردائية وغير مانعة فإن تطبيقك أيضًا ليس ردائيًا حقًا. على سبيل المثال، لا تحتوي بائعي قواعد البيانات العلاقية على مشغلات ردائية لأنهم يعتمدون على JDBC، وهو غير ردائي. وبالتالي، واجهة برمجة التطبيقات Hibernate أيضًا غير ردائية. لذا إذا كنت تستخدم قواعد بيانات علاقية فلا يمكنك بناء تطبيق ردائيًا حقًا بعد. أنا متفائل بأن هذا سيتغير قريبًا أكثر مما لاحقًا.
يمكنك تنزيل كود المشروع من مستودعي على GitHub.
المرجع: التوثيق الرسمي
Source:
https://www.digitalocean.com/community/tutorials/spring-webflux-reactive-programming