開篇詞
該指南將引導你創建 “Hello, Spring!”。帶有 Spring WebFlux(版本 5 的新功能)的 RESTful Web 服務,然後通過 WebClient(版本 5 的新功能)使用該服務。
該指南展示了使用 Spring WebFlux 的功能方法。你可以在 WebFlux 中使用註解。
你將創建的應用
我們將使用 Spring WebFlux 和該服務的 WebClient 使用者構建 RESTful 網絡服務。我們可以在 System.out 和以下位置看到輸出:
http://localhost:8080/hello
你將需要的工具
- 大概 15 分鐘左右;
- 你最喜歡的文本編輯器或集成開發環境(IDE)
- JDK 1.8 或更高版本;
- Gradle 4+ 或 Maven 3.2+
- 你還可以將代碼直接導入到 IDE 中:
如何完成這個指南
像大多數的 Spring 入門指南一樣,你可以從頭開始並完成每個步驟,也可以繞過你已經熟悉的基本設置步驟。如論哪種方式,你最終都有可以工作的代碼。
- 要從頭開始,移步至用 Gradle 來構建;
- 要跳過基礎,執行以下操作:
- 下載並解壓縮該指南將用到的源代碼,或藉助 Git 來對其進行克隆操作:
git clone https://github.com/spring-guides/gs-reactive-rest-service.git
- 切換至
gs-reactive-rest-service/initial
目錄; - 跳轉至該指南的創建 WebFlux 句柄。
- 下載並解壓縮該指南將用到的源代碼,或藉助 Git 來對其進行克隆操作:
待一切就緒後,可以檢查一下 gs-reactive-rest-service/complete
目錄中的代碼。
用 Gradle 來構建
首先,我們設置一個基本的構建腳本。在使用 Spring 構建應用時可以使用任何喜歡的構建系統,但此處包含使用 Gradle 和 Maven 所需的代碼。如果你都不熟悉,請參閱使用 Gradle 構建 Java 項目或使用 Maven 構建 Java 項目。
創建目錄結構
在我們選擇的項目目錄中,創建以下自目錄結構;例如,在 *nix 系統上使用 mkdir -p src/main/java/hello
:
└── src
└── main
└── java
└── hello
創建 Gradle 構建文件
以下是初始 Gradle 構建文件。
build.gradle
buildscript {
ext {
springBootVersion = '2.2.4.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'gs-reactive-rest-service'
version = '0.1.0'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-webflux')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('io.projectreactor:reactor-test')
}
Spring Boot gradle 插件提供了許多方便的功能:
- 它收集類路徑上的所有 jar,並構建一個可運行的單個超級 jar,這使執行和傳輸服務更加方便;
- 它搜索
public static void main()
方法並將其標記爲可運行類; - 它提供了一個內置的依賴解析器,用於設置版本號以及匹配 Spring Boot 依賴。我們可以覆蓋所需的任何版本,但默認爲 Boot 選擇的一組版本。
用 Maven 來構建
首先,我們搭建一個基本的構建腳本。使用 Spring 構建應用時,可以使用任何喜歡的構建系統,但是此處包含了使用 Maven 所需的代碼。如果你不熟悉 Maven,請參閱使用 Maven 構建 Java 項目。
創建目錄結構
在我們選擇的項目目錄中,創建以下自目錄結構;例如,在 *nix 系統上使用 mkdir -p src/main/java/hello
:
└── src
└── main
└── java
└── hello
創建 Maven 構建文件
以下是初始 Maven 構建文件。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-reactive-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.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>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot Maven 插件提供了許多方便的功能:
- 它收集類路徑上的所有 jar,並構建一個可運行的單個超級 jar,這使執行和傳輸服務更加方便;
- 它搜索
public static void main()
方法並將其標記爲可運行類; - 它提供了一個內置的依賴解析器,用於設置版本號以及匹配 Spring Boot 依賴。我們可以覆蓋所需的任何版本,但默認爲 Boot 選擇的一組版本。
用 IDE 來構建
- 閱讀如何將該指南直接導入 Spring Tool Suite;
- 閱讀如何在 IntelliJ IDEA 中使用該指南。
創建 WebFlux 句柄
在 Spring Reactive 方法中,我們使用處理程序來處理請求並創建響應,如下示例所示:
src/main/java/hello/GreetingHandler.java
package hello;
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 GreetingHandler {
public Mono<ServerResponse> hello(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromValue("Hello, Spring!"));
}
}
這個簡單的反應式類始終返回 “Hello, Spring!”。它可能返回許多其他內容,包括來自數據庫的項目流,通過計算生成的項目流,等等。請注意反應式代碼:一個包含 ServerResponse
主體的 Mono
對象。
創建路由
在該應用中,我們使用路由來處理我們公開的唯一路由("/hello"),如以下示例所示:
src/main/java/hello/GreetingRouter.java
package hello;
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 GreetingRouter {
@Bean
public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
return RouterFunctions
.route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), greetingHandler::hello);
}
}
路由在 /hello
路徑上偵聽流量,並返回我們的反應式處理程序類提供的值。
創建 WebClient
Spring MVC RestTemplate 類本質上是阻塞的。因此,我們不想在反應式應用中使用它。對於反應式應用,Spring 提供了非阻塞的 WebClient 類。我們將使用 WebClient 實現來使用我們的 RESTful 服務:
src/main/java/hello/GreetingWebClient.java
package hello;
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 GreetingWebClient {
private WebClient client = WebClient.create("http://localhost:8080");
private Mono<ClientResponse> result = client.get()
.uri("/hello")
.accept(MediaType.TEXT_PLAIN)
.exchange();
public String getResult() {
return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
}
}
WebClient 使用反應式功能,以 Mono 的形式保存我們指定的 URI 的內容,並使用一個函數(在 getResult
方法中)將該內容轉換爲字符串。如果我們有不同的要求,則可以將其轉換爲字符串以外的形式。由於我們要將結果放入 System.out,因此這裏將使用字符串。
WebClient 也可以用於非反應式阻塞服務進行通信。
使應用可執行
儘管可以將服務打包爲傳統的 WAR 文件以部署到外部應用服務器,但是下面演示的更簡單的方法創建了一個獨立的應用。我們將所有內容打包在一個可執行的 JAR 文件中,由一個經典的 Java main() 方法驅動。在該過程中,我們使用 Reactive Spring 的支持將 Netty 服務器作爲 HTTP 運行時嵌入,而不是部署到外部實例。
src/main/java/hello/Application.java
package hello;
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);
GreetingWebClient gwc = new GreetingWebClient();
System.out.println(gwc.getResult());
}
}
@SpringBootApplication
是一個便利的註解,它添加了以下所有內容:
@Configuration
:將類標註爲應用上下文 Bean 定義的源;@EnableAutoConfiguration
:告訴 Spring Boot 根據類路徑配置、其他 bean 以及各種屬性的配置來添加 bean;@ComponentScan
:告知 Spring 在hello
包中尋找他組件、配置以及服務。
main()
方法使用 Spring Boot 的 SpringApplication.run()
方法啓動應用。
構建可執行 JAR
我們可以結合 Gradle 或 Maven 來從命令行運行該應用。我們還可以構建一個包含所有必須依賴項、類以及資源的可執行 JAR 文件,然後運行該文件。在整個開發生命週期中,跨環境等等情況下,構建可執行 JAR 可以輕鬆地將服務作爲應用進行發佈、版本化以及部署。
如果使用 Gradle,則可以藉助 ./gradlew bootRun
來運行應用。或通過藉助 ./gradlew build
來構建 JAR 文件,然後運行 JAR 文件,如下所示:
java -jar build/libs/gs-reactive-rest-service-0.1.0.jar
如果使用 Maven,則可以藉助 ./mvnw spring-boot:run
來運行該用。或可以藉助 ./mvnw clean package
來構建 JAR 文件,然後運行 JAR 文件,如下所示:
java -jar target/gs-reactive-rest-service-0.1.0.jar
我們還可以將 JAR 應用轉換成 WAR 應用。
顯示日誌記錄輸出。該服務應在幾秒內啓動並運行。
服務啓動後,我們會看到以下內容:
>> result = Hello, Spring!
改行來自 WebClient 正在使用的反應式內容。自然,與將輸出放入 System.out 相比,我們會發現輸出更有趣。
測試應用
現在該應用正在運行,我們可以對其進行測試。首先,我們可以打開瀏覽器並轉到 http://localhost:8080/hello,然後看到 “Hello, Spring!”。對於該指南,我們還創建了一個測試類,以使我們開始使用 WebTestClient 類進行測試。
src/test/java/hello/GreetingRouterTest.java
package hello;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
@ExtendWith(SpringExtension.class)
// We create a `@SpringBootTest`, starting an actual server on a `RANDOM_PORT`
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GreetingRouterTest {
// Spring Boot will create a `WebTestClient` for you,
// already configure and ready to issue requests against "localhost:RANDOM_PORT"
@Autowired
private WebTestClient webTestClient;
@Test
public void testHello() {
webTestClient
// Create a GET request to test an endpoint
.get().uri("/hello")
.accept(MediaType.TEXT_PLAIN)
.exchange()
// and use the dedicated DSL to test assertions against the response
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello, Spring!");
}
}
概述
恭喜你!我們已經開發了一個 Reactive Spring 應用,其中包括一個 WebClient 來使用 RESTful 服務!
想看指南的其他內容?請訪問該指南的所屬專欄:《Spring 官方指南》