建構反應式 RESTful Web 服務

本指南將引導您使用 Spring WebFlux(Spring Boot 2.0 的新功能)建立「Hello, Spring!」RESTful Web 服務,然後使用 WebClient(也是 Spring Boot 2.0 的新功能)使用該服務。

本指南展示了使用 Spring WebFlux 的函數式方法。您也可以使用帶有 WebFlux 的註解

您將建構什麼

您將建構一個具有 Spring Webflux 的 RESTful Web 服務和一個 WebClient 的消費者服務。您將能夠在 System.out 和

https://127.0.0.1:8080/hello

您需要什麼

如何完成本指南

與大多數 Spring 入門指南一樣,您可以從頭開始並完成每個步驟,或者您可以跳過您已經熟悉的基礎設定步驟。 無論哪種方式,您最終都會得到可運作的程式碼。

從頭開始,請移至 使用 Spring Initializr 開始

跳過基礎知識,請執行以下操作

當您完成時,您可以將您的結果與 gs-reactive-rest-service/complete 中的程式碼進行比較。

使用 Spring Initializr 開始

您可以使用這個 預先初始化的專案,然後點擊 Generate (產生) 下載 ZIP 檔案。 此專案已配置為適合本教學中的範例。

手動初始化專案

  1. 導覽至 https://start.spring.io。 此服務會提取應用程式所需的所有依賴項,並為您完成大部分設定。

  2. 選擇 Gradle 或 Maven 以及您想要使用的語言。 本指南假設您選擇 Java。

  3. 點擊 Dependencies (依賴項) 並選擇 Spring Reactive Web

  4. 點擊 Generate (產生)。

  5. 下載產生的 ZIP 檔案,這是一個使用您選擇的配置的 Web 應用程式的存檔。

如果您的 IDE 有 Spring Initializr 整合,您可以從您的 IDE 完成此過程。
您也可以從 Github 分叉專案,並在您的 IDE 或其他編輯器中打開它。

建立 WebFlux Handler

我們將從一個 Greeting POJO 開始,它將由我們的 RESTful 服務序列化為 JSON

src/main/java/hello/Greeting.java

package com.example.reactivewebservice;


public class Greeting {

  private String message;

  public Greeting() {
  }

  public Greeting(String message) {
    this.message = message;
  }

  public String getMessage() {
    return this.message;
  }

  public void setMessage(String message) {
    this.message = message;
  }

  @Override
  public String toString() {
    return "Greeting{" +
        "message='" + message + '\'' +
        '}';
  }
}

在 Spring Reactive 方法中,我們使用處理器來處理請求並建立回應,如下例所示

src/main/java/hello/GreetingHandler.java

package com.example.reactivewebservice;

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.APPLICATION_JSON)
      .body(BodyInserters.fromValue(new Greeting("Hello, Spring!")));
  }
}

這個簡單的反應式類別總是傳回一個具有 "Hello, Spring!" 問候語的 JSON 主體。它可以傳回許多其他東西,包括來自資料庫的項目流、由計算產生的項目流等等。請注意反應式程式碼:一個保存 ServerResponse 主體的 Mono 物件。

建立路由器

在本應用程式中,我們使用路由器來處理我們公開的唯一路由 (/hello),如下例所示

src/main/java/hello/GreetingRouter.java

package com.example.reactivewebservice;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;

@Configuration(proxyBeanMethods = false)
public class GreetingRouter {

  @Bean
  public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {

    return RouterFunctions
      .route(GET("/hello").and(accept(MediaType.APPLICATION_JSON)), greetingHandler::hello);
  }
}

路由器監聽 /hello 路徑上的流量,並傳回我們的反應式處理器類別提供的值。

建立 WebClient

Spring RestTemplate 類別本質上是阻塞的。因此,我們不想在反應式應用程式中使用它。對於反應式應用程式,Spring 提供了 WebClient 類別,它是非阻塞的。我們使用基於 WebClient 的實現來使用我們的 RESTful 服務

src/main/java/hello/GreetingClient.java

package com.example.reactivewebservice;

import reactor.core.publisher.Mono;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;

@Component
public class GreetingClient {

  private final WebClient client;

  // Spring Boot auto-configures a `WebClient.Builder` instance with nice defaults and customizations.
  // We can use it to create a dedicated `WebClient` for our component.
  public GreetingClient(WebClient.Builder builder) {
    this.client = builder.baseUrl("https://127.0.0.1:8080").build();
  }

  public Mono<String> getMessage() {
    return this.client.get().uri("/hello").accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(Greeting.class)
        .map(Greeting::getMessage);
  }

}

WebClient 類別使用反應式特性,以 Mono 的形式保存訊息的內容(由 getMessage 方法傳回)。這是使用函數 API,而不是命令式 API,來鏈接反應式運算符。

可能需要時間來習慣 Reactive APIs,但是 WebClient 具有有趣的功能,並且也可以在傳統的 Spring MVC 應用程式中使用。

您也可以使用 WebClient 與非反應式、阻塞的服務進行通信。

使應用程式可執行

我們將使用 main() 方法來驅動我們的應用程式並從我們的端點取得 Greeting 訊息。

src/main/java/hello/Application.java

package com.example.reactivewebservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class ReactiveWebServiceApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(ReactiveWebServiceApplication.class, args);
    GreetingClient greetingClient = context.getBean(GreetingClient.class);
    // We need to block for the content here or the JVM might exit before the message is logged
    System.out.println(">> message = " + greetingClient.getMessage().block());
  }
}

@SpringBootApplication 是一個方便的註解,它增加了以下所有內容

  • @Configuration:將該類別標記為應用程式上下文的 Bean 定義來源。

  • @EnableAutoConfiguration:告訴 Spring Boot 根據類別路徑設定、其他 Bean 和各種屬性設定開始新增 Bean。例如,如果 spring-webmvc 在類別路徑上,則此註解將應用程式標記為 Web 應用程式並激活關鍵行為,例如設定 DispatcherServlet

  • @ComponentScan:告訴 Spring 在 hello 封裝中尋找其他元件、配置和服務,讓它找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法來啟動應用程式。您是否注意到沒有任何 XML 程式碼?也沒有 web.xml 檔案。此 Web 應用程式是 100% 純 Java,您無需處理配置任何基礎結構。

建構可執行 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 檔案

顯示日誌輸出。該服務應在幾秒鐘內啟動並執行。

服務啟動後,您可以看到一行讀取

>> message = Hello, Spring!

該行來自 WebClient 使用的反應式內容。當然,您可以找到比將其放入 System.out 中更有趣的事情來處理您的輸出。

測試應用程式

現在應用程式正在執行,您可以測試它。首先,您可以打開瀏覽器並前往 https://127.0.0.1:8080/hello 並看到「Hello, Spring!」。對於本指南,我們還建立了一個測試類別,讓您可以開始使用 WebTestClient 類別進行測試。

src/test/java/hello/GreetingRouterTest.java

package com.example.reactivewebservice;

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;

import static org.assertj.core.api.Assertions.assertThat;

@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.APPLICATION_JSON)
      .exchange()
      // and use the dedicated DSL to test assertions against the response
      .expectStatus().isOk()
      .expectBody(Greeting.class).value(greeting -> {
        assertThat(greeting.getMessage()).isEqualTo("Hello, Spring!");
    });
  }
}

總結

恭喜!您已經開發了一個包含 WebClient 的 Reactive Spring 應用程式來使用 RESTful 服務!

想要編寫新指南或貢獻現有指南?查看我們的貢獻指南

所有指南均以 ASLv2 許可證發布用於程式碼,並以 歸屬、禁止衍生創作的 Creative Commons 許可證發布用於寫作。

取得程式碼