欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring WebFlux之響應(yīng)式編程詳解

 更新時間:2025年03月25日 17:04:35   作者:春哥的魔法書  
這篇文章主要介紹了Spring WebFlux之響應(yīng)式編程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

在軟件開發(fā)領(lǐng)域,隨著互聯(lián)網(wǎng)應(yīng)用的規(guī)模和復(fù)雜性不斷增加,傳統(tǒng)的編程模型逐漸暴露出一些局限性,尤其是在面對高并發(fā)、大規(guī)模數(shù)據(jù)流處理等場景時。

為了應(yīng)對這些挑戰(zhàn),響應(yīng)式編程(Reactive Programming)應(yīng)運而生,它提供了一種更為高效、靈活的編程范式,以適應(yīng)不斷變化的系統(tǒng)需求。

1.Spring WebFlux 簡介

WebFlux提供了一個非阻塞、異步的Web框架,允許開發(fā)者構(gòu)建高性能、可伸縮的 Web 應(yīng)用程序,特別適合處理大量并發(fā)連接,如在微服務(wù)架構(gòu)和云環(huán)境中。

WebFlux是Spring Framework 5引入的一個重要組件,它代表了Spring對于響應(yīng)式編程(Reactive Programming)的支持。

1.1.異步非阻塞

異步非阻塞是一種編程模式,它允許程序在等待某個操作完成時繼續(xù)執(zhí)行其他任務(wù)。這種模式是基于事件循環(huán),可以在單個線程上處理多個I/O操作,大幅提高了系統(tǒng)的吞吐量和伸縮性。

  • 服務(wù)器端處理: WebFlux 使用 Netty 或 Undertow 這類非阻塞服務(wù)器,它們能夠處理大量連接而不阻塞線程。這意味著服務(wù)器可以在單個線程中同時處理多個請求,提高了資源利用率和吞吐量。
  • 數(shù)據(jù)庫訪問: 通過使用 R2DBC( Reactive Relational Database Connectivity)或其他支持響應(yīng)式編程的數(shù)據(jù)庫客戶端,可以在數(shù)據(jù)庫查詢中實現(xiàn)異步操作,從而避免線程等待數(shù)據(jù)庫響應(yīng)造成的阻塞。
  • 異步API調(diào)用: 在處理外部服務(wù)調(diào)用時,可以利用 WebClient 進行異步HTTP請求,WebClient 是完全非阻塞的,能夠在等待響應(yīng)的同時處理其他任務(wù)。

1.2.響應(yīng)式流(Reactive Streams)

響應(yīng)式流是一個規(guī)范,它定義了異步流處理的接口和行為。

1.2.1.響應(yīng)式編程

響應(yīng)式編程是一種編程范式,是一種異步的、事件驅(qū)動的編程范式,它特別適用于構(gòu)建能夠處理實時數(shù)據(jù)流的應(yīng)用程序。在這種模型中,數(shù)據(jù)和事件作為流進行處理,允許開發(fā)者以聲明式的方式構(gòu)建復(fù)雜的異步邏輯。

Spring WebFlux 遵循這一規(guī)范,使用 Publisher 作為響應(yīng)式流的源頭,Subscriber 作為流的消費者。這種模型允許開發(fā)者以聲明式的方式處理異步數(shù)據(jù)流,同時保持了流的控制和背壓管理。

  • 數(shù)據(jù)流處理: Spring WebFlux 集成了 Reactor 庫,使用 Flux 和 Mono 類型來處理數(shù)據(jù)流。Flux 表示0到N個元素的異步序列,Mono 表示0或1個元素的異步結(jié)果,兩者都支持背壓策略,能夠智能調(diào)整數(shù)據(jù)生產(chǎn)速度以匹配消費者的處理能力。
  • 事件驅(qū)動編程: 應(yīng)用程序可以輕松地處理各種事件,如消息隊列中的消息、WebSocket連接上的事件等,通過響應(yīng)式流模型,可以高效地處理這些事件而不會阻塞主線程。

1.2.2.背壓管理

  • 背壓是響應(yīng)式流中的一個重要概念,用于控制生產(chǎn)者和消費者之間的數(shù)據(jù)流速率。
  • Project Reactor 提供了多種背壓策略,幫助開發(fā)者處理數(shù)據(jù)流過載的問題。

1.2.3.Project Reactor

  • Project Reactor 用于創(chuàng)建和操作響應(yīng)式流的一組豐富的API。
  • Project Reactor 是一個基于Java 8的響應(yīng)式編程庫,由Pivotal團隊開發(fā),專為配合Spring框架使用而設(shè)計。

響應(yīng)式類型:Mono 和 Flux

  • Mono:代表0到1個元素的響應(yīng)式類型,適合表示單個結(jié)果或無結(jié)果的異步操作。
  • Flux:代表0到N個元素的響應(yīng)式類型,用于表示多個結(jié)果的異步序列。

操作符與響應(yīng)式數(shù)據(jù)流

Project Reactor 提供了大量操作符,用于處理響應(yīng)式流中的元素。這些操作符包括:

  • Map:將流中的每個元素應(yīng)用一個函數(shù),并發(fā)布結(jié)果。
  • Filter:根據(jù)條件過濾流中的元素。
  • FlatMap:將流中的每個元素轉(zhuǎn)換為另一個流,并將結(jié)果流合并為一個流。
  • SwitchIfEmpty:如果源流為空,則切換到備選的Mono或Flux。

1.2.4.與傳統(tǒng)Spring MVC的比較

Spring MVC是一個基于Servlet API的Web框架,它采用阻塞I/O模型,每個請求/響應(yīng)對都與一個線程綁定。這在并發(fā)量較低時表現(xiàn)良好,但在高并發(fā)場景下,線程資源的消耗會急劇增加。

相比之下,Spring WebFlux基于響應(yīng)式流,它不依賴于Servlet API,可以在如Netty、Undertow等非Servlet服務(wù)器上運行。這種模型使得WebFlux能夠以非阻塞的方式處理并發(fā)請求,有效利用資源,提高性能。

1.3.函數(shù)式編程

函數(shù)式編程是一種編程范式 , 它強調(diào)的是將任務(wù)看作一系列可組合的函數(shù)調(diào)用。通過聲明式的方式定義處理流程,讓代碼更簡潔、易讀,也更適合處理復(fù)雜的異步邏輯。WebFlux采用函數(shù)式編程范式,利用Lambda表達式簡化了編程模型,路由和請求處理采用函數(shù)式編程的方式進行定義,這與傳統(tǒng)的基于注解的控制器方法截然不同。

  • 路由與處理: WebFlux 提供了函數(shù)式編程模型,允許開發(fā)者使用 Java 8 的 Lambda 表達式來定義路由規(guī)則和處理函數(shù),使得代碼更為簡潔、可讀性強。例如,可以使用 RouterFunctions.route() 方法來定義路由,使用 ServerResponse 來構(gòu)建響應(yīng)。
  • 鏈式操作與組合: 利用響應(yīng)式類型 Flux 和 Mono 的豐富操作符,如 map(), filter(), flatMap() 等,可以輕松地構(gòu)建復(fù)雜的異步數(shù)據(jù)處理流程,而無需顯式管理回調(diào)或線程。

1.3.1.請求路由

使用 RouterFunction 定義請求路由

RouterFunction 是Spring WebFlux中用于定義請求路由的函數(shù)接口。通過實現(xiàn) RouterFunction,可以精確控制請求的匹配和處理。

路由謂語和處理器

  • 路由謂語:用于匹配HTTP請求的特定屬性,如路徑、方法、頭部等。
  • 處理器:一旦路由謂語匹配成功,處理器將負責(zé)處理請求并返回響應(yīng)。

1.3.2.函數(shù)式端點

Spring WebFlux 還引入了函數(shù)式端點的概念,允許開發(fā)者以簡單的函數(shù)形式處理請求和生成響應(yīng),這些函數(shù)通常返回 ServerResponse。

1.3.3.函數(shù)式編程與響應(yīng)式流

函數(shù)式編程是響應(yīng)式編程模型的一個重要組成部分。它提倡使用無副作用的函數(shù)、不可變數(shù)據(jù)結(jié)構(gòu),并且推崇聲明式編程。

這些原則與響應(yīng)式流的概念相契合,響應(yīng)式流強調(diào)數(shù)據(jù)流的聲明式處理,以及在數(shù)據(jù)流中應(yīng)用各種操作符來轉(zhuǎn)換、過濾和組合數(shù)據(jù)。

2.Spring WebFlux 應(yīng)用搭建

2.1 環(huán)境準備

項目依賴配置

基于Maven的Springboot項目,pom.xml文件中的依賴配置可能如下所示:

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

2.2 定義路由與處理器

2.2.1.創(chuàng)建 RouterFunction Bean

在Spring WebFlux中,使用RouterFunction來定義請求的路由。

首先,創(chuàng)建一個配置類,并在其中定義路由:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration
public class WebFluxConfig {

    @Bean
    public RouterFunction<ServerResponse> route(MyHandler handler) {
        
        return RouterFunctions.route(GET("/hello"), handler::hello);
    }
}
  • RouterFunctions.route() 是用來創(chuàng)建路由規(guī)則的起點。
  • GET("/hello") 是來自RequestPredicates的靜態(tài)方法,定義了一個謂詞,用于匹配HTTP GET方法并且路徑為"/hello"的請求。
  • handler::hello 是一個方法引用,指向MyHandler類中名為hello的方法。這意味著當(dāng)匹配到上述HTTP請求條件時,會調(diào)用handler.hello()方法來處理請求,并期待它返回一個ServerResponse對象作為響應(yīng)。
2.2.2.使用 HandlerFunction 處理請求

創(chuàng)建一個處理器類,它將包含處理請求的方法。

這些方法可以返回Mono<ServerResponse>Flux<ServerResponse>,這取決于它們處理的是單個響應(yīng)還是響應(yīng)流:

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
public class MyHandler {

    public Mono<ServerResponse> hello(ServerRequest request) {
        String name = request.queryParam("name").orElse("World");
        String message = "Hello, " + name + "!";
        return ServerResponse.ok()
                .contentType(MediaType.TEXT_PLAIN)
                .body(Mono.just(message), String.class);
    }
}

hello的方法,用于處理HTTP請求并返回一個響應(yīng)。

  • Mono<ServerResponse>:返回類型是一個Mono對象,這是Reactor庫中的一個類,用于表示0到1個元素的異步序列。在這里,它最終會包含一個ServerResponse對象,即HTTP響應(yīng)。使用Mono是為了支持非阻塞和響應(yīng)式編程。
  • ServerRequest request:輸入?yún)?shù),表示接收到的HTTP請求信息。

提取查詢參數(shù):

String name = request.queryParam("name").orElse("World");
  • 這行代碼從ServerRequest對象中嘗試獲取名為"name"的查詢參數(shù)。
  • 如果請求中包含了這個參數(shù),比如http://example.com/hello?name=John,那么name變量就會被賦值為"John";如果沒有提供,則使用默認值"World"。

構(gòu)建HTTP響應(yīng)的:

  • ServerResponse.ok():創(chuàng)建一個表示成功(HTTP狀態(tài)碼200 OK)的基礎(chǔ)響應(yīng)。
  • .contentType(MediaType.TEXT_PLAIN):設(shè)置響應(yīng)的內(nèi)容類型為純文本(PLAIN TEXT)。
  • .body(Mono.just(message), String.class):指定響應(yīng)體的內(nèi)容。這里使用Mono.just(message)來包裝問候消息字符串,表明響應(yīng)體是一個包含單個元素的異步序列,其類型為String。這確保了整個處理過程是非阻塞的。

2.3 全局異常處理

全局異常處理是任何Web應(yīng)用程序的重要部分

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {

    public Mono<Void> handleException(ServerWebExchange exchange, Throwable ex) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        DataBuffer buffer = response.bufferFactory().wrap("{\"error\": \"Internal Server Error\"}".getBytes());
        return response.writeWith(Mono.just(buffer));
    }
}

這段代碼定義了一個全局異常處理器,用于在Spring WebFlux應(yīng)用中捕獲未處理的異常,并向客戶端返回統(tǒng)一的錯誤響應(yīng)。下面是各部分的詳細說明:

@Order(Ordered.HIGHEST_PRECEDENCE)

  • @Order注解用于指定組件的執(zhí)行順序,特別是當(dāng)有多個相同類型的組件(比如多個異常處理器)需要按特定順序執(zhí)行時。Ordered.HIGHEST_PRECEDENCE是一個常量,值為Integer.MIN_VALUE,意味著這個異常處理器將具有最高優(yōu)先級,會在所有其他相同類型的組件之前執(zhí)行。換句話說,如果有其他異常處理器也能夠處理相同的異常類型,但沒有指定這么高的優(yōu)先級,那么這個GlobalExceptionHandler將會優(yōu)先處理異常。

handleException 方法

參數(shù)

  • ServerWebExchange exchange:封裝了HTTP請求和響應(yīng)的所有信息,是WebFlux處理請求時的核心對象。
  • Throwable ex:拋出的異常對象,即需要被處理的異常。

功能

  • 設(shè)置響應(yīng)狀態(tài)碼:首先通過exchange.getResponse()獲取響應(yīng)對象,并將其狀態(tài)碼設(shè)置為HttpStatus.INTERNAL_SERVER_ERROR(HTTP 500),表示服務(wù)器遇到了未知錯誤。
  • 構(gòu)建響應(yīng)體:使用response.bufferFactory().wrap()方法創(chuàng)建一個包含錯誤信息的DataBuffer對象。這里的消息體內(nèi)容為{"error": "Internal Server Error"},表示發(fā)生了內(nèi)部服務(wù)器錯誤。
  • 寫入響應(yīng)體并完成響應(yīng):最后,通過response.writeWith(Mono.just(buffer))將構(gòu)建好的錯誤信息緩沖區(qū)寫入響應(yīng),并返回一個Mono<Void>表示這是一個無返回值的異步操作,完成響應(yīng)的發(fā)送。

3.應(yīng)用細節(jié)

3.1.RouterFunction

RouterFunction 是 Spring WebFlux 提供的一種用于定義和處理 HTTP 路由的功能性接口,它是構(gòu)建響應(yīng)式 Web 應(yīng)用的基礎(chǔ)組件之一。與傳統(tǒng)的基于注解(如 @GetMapping, @PostMapping 等)的控制器相比,RouterFunction 提供了一種更為靈活和強大的方式來定義路由邏輯,特別適合函數(shù)式編程風(fēng)格。

下面詳細介紹其用法:

3.1.1. 基本概念

  • RouterFunction: 表示一個路由處理邏輯,它將HTTP請求與相應(yīng)的處理邏輯關(guān)聯(lián)起來。一個RouterFunction可以匹配一個請求,返回下一個RouterFunction或一個處理結(jié)果(HandlerFunction)。
  • HandlerFunction: 表示一個處理邏輯,它接受一個ServerRequest并返回一個Mono<ServerResponse>。即,它負責(zé)處理請求并產(chǎn)生響應(yīng)。

3.1.2. 創(chuàng)建 RouterFunction

創(chuàng)建 RouterFunction 通常涉及定義路由規(guī)則和處理邏輯。

以下是一個簡單的示例,展示如何定義一個路由來處理 GET 請求:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
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.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@Configuration
public class GreetingRouter {
    @Bean
    public  RouterFunction<ServerResponse> routingFunction() {
        return route(GET("/hello2"), request -> ok().bodyValue("Hello, Spring WebFlux!"));
    }
}

這段代碼定義了一個簡單的RouterFunction<ServerResponse>實例,用于處理一個HTTP GET請求到/hello路徑的路由邏輯。

return route(GET("/hello"), request -> ok().bodyValue("Hello, Spring WebFlux!"));

routeRouterFunctions類中的一個靜態(tài)方法,用于創(chuàng)建一個基礎(chǔ)的路由定義。它接受兩個參數(shù):

  • 謂詞(Predicate)GET("/hello")是一個謂詞,用于匹配HTTP請求的方法和路徑。這里匹配所有GET方法并且路徑為/hello的請求。
  • 處理器函數(shù)(HandlerFunction)request -> ok().bodyValue("Hello, Spring WebFlux!")是一個Lambda表達式,代表了處理請求的具體邏輯。它接受一個ServerRequest對象作為輸入,并返回一個Mono<ServerResponse>表示響應(yīng)結(jié)果。

request -> ...:Lambda表達式定義了如何根據(jù)請求生成響應(yīng)。

ok():這是ServerResponse的靜態(tài)工廠方法,用于創(chuàng)建一個表示成功(HTTP狀態(tài)碼200 OK)的響應(yīng)。

.bodyValue("Hello, Spring WebFlux!"):此方法設(shè)置了響應(yīng)體的內(nèi)容為給定的字符串,并且指定了內(nèi)容類型(默認情況下,如果沒有顯式指定,會根據(jù)內(nèi)容推斷)。

3.1.3. 注冊 RouterFunction

要在 Spring Boot 應(yīng)用中注冊 RouterFunction,通常在配置類中聲明為 @Bean,以便 Spring 自動發(fā)現(xiàn)并配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 WebConfig {

    @Bean
    public RouterFunction<ServerResponse> routes(GreetingHandler handler) {
        return RouterFunctions.route(GET("/hello"), handler::sayHello);
    }
}

其中,GreetingHandler 是一個包含業(yè)務(wù)邏輯的 HandlerFunction。

3.1.4. 組合 RouterFunction

RouterFunction 可以組合起來形成復(fù)雜的路由結(jié)構(gòu)。這使得路由配置更加模塊化和可維護。

  • 謂詞組合:可以使用 and, or 等邏輯運算符組合謂詞,以實現(xiàn)更復(fù)雜的路由匹配邏輯。
    @Bean
    public RouterFunction<ServerResponse> route(MyHandler handler) {
        return RouterFunctions.route(GET("/haha")
                .and(accept(MediaType.TEXT_PLAIN)), handler::haha)

                .andRoute(GET("/reactor")
                .and(accept(MediaType.APPLICATION_JSON)), handler::reactorExample);
    }
    // 處理簡單的文本請求
    public Mono<ServerResponse> haha(ServerRequest request) {
        return ServerResponse.ok()
                .contentType(MediaType.TEXT_PLAIN)
                .bodyValue("haha!");
    }

    // 處理復(fù)雜的響應(yīng)式請求
    public Mono<ServerResponse> reactorExample(ServerRequest request) {
        return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue("{'message': 'This is a reactive response!'}");
    }

3.1.5. 處理請求參數(shù)

RouterFunction 可以方便地處理請求參數(shù),無論是路徑參數(shù)、查詢參數(shù)還是請求體。例如,處理帶路徑參數(shù)的請求:

public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
    return route(GET("/users/{id}"), handler::getUserById);
}

3.1.6. 錯誤處理

Spring WebFlux 支持全局或局部的錯誤處理,可以通過提供一個處理特定異常類型的 RouterFunction 實現(xiàn):

3.1.6.1.自帶異常處理

    @Bean
    public RouterFunction<ServerResponse> route(MyHandler handler) {
        return RouterFunctions.route()
                .GET("/greeting", handler::reactorExample)
                // 全局錯誤處理
                .onError(Exception.class, (exception, request) -> {
                    System.out.println("An exception occurred: " + exception.getMessage());
                    return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
                            .contentType(MediaType.TEXT_PLAIN)
                            .bodyValue("Oops! Something went wrong.");
                })
     
                .build();
    }

3.1.6.2.全局異常處理

    // 錯誤處理路由
    private RouterFunction<ServerResponse> errorRoute() {
        return RouterFunctions.route(RequestPredicates.all(),
                request -> ServerResponse.status(HttpStatus.BAD_REQUEST)
                        .contentType(MediaType.TEXT_PLAIN)
                        .bodyValue("這是一條用于未匹配請求的回退處理。"));
    }

RequestPredicates.all(),意味著這個謂詞將匹配所有的HTTP請求,無論方法(GET、POST等)或路徑是什么。

3.1.7. 高級用法

  • 過濾器與攔截器:可以插入自定義的過濾器或攔截器來處理請求的預(yù)處理或后處理邏輯。
  • 條件路由:基于環(huán)境、配置或其他條件動態(tài)選擇路由。

3.2.請求接參

Spring WebFlux 完全支持接收 RESTful 風(fēng)格的傳參。RESTful 風(fēng)格的接口通常通過URL路徑、查詢參數(shù)、請求頭以及請求體來傳遞參數(shù)。

在Spring WebFlux中,你可以使用函數(shù)式和注解兩種方式來定義端點以接收這些參數(shù)。下面是幾種常見的接收參數(shù)方式:

3.2.1. 路徑變量(Path Variables)

在路由定義中,你可以使用 {variableName} 來標記路徑變量,然后在處理器方法中通過 @PathVariable 注解接收它們。

@Bean
public RouterFunction<ServerResponse> userRoute(UserHandler handler) {
    return RouterFunctions.route(GET("/users/{id}"), handler::getUserById);
}

@Component
public class UserHandler {

    public Mono<ServerResponse> getUserById(ServerRequest request) {
        String id = request.pathVariable("id");
        // 處理邏輯...
    }
}

3.2.2. 查詢參數(shù)(Query Parameters)

查詢參數(shù)可以直接從 ServerRequest 中獲取,或者使用 @RequestParam 注解。

public Mono<ServerResponse> searchUsers(ServerRequest request) {
    String keyword = request.queryParam("keyword").orElse("");
    // 處理邏輯...
}

或者使用 @RequestParam

public Mono<ServerResponse> searchUsers(@RequestParam(name = "keyword", defaultValue = "") String keyword) {
    // 處理邏輯...
}

3.2.3. 請求體(Request Body)

對于POST、PUT等方法,你可能需要從請求體中讀取數(shù)據(jù)??梢允褂?@RequestBody 注解,并指定相應(yīng)的對象類型來自動綁定JSON或表單數(shù)據(jù)。

public Mono<ServerResponse> createUser(@RequestBody UserDTO user) {
    // 處理邏輯...
}

3.2.4. 請求頭(Request Headers)

請求頭可以通過 ServerRequest.headers() 獲取,或者使用 @RequestHeader 注解。

public Mono<ServerResponse> handleRequestWithHeader(@RequestHeader("Authorization") String authHeader) {
    // 處理邏輯...
}

3.3.響應(yīng)內(nèi)容

3.3.1.ServerResponse對象

在Spring WebFlux中,響應(yīng)的返回內(nèi)容通常是通過ServerResponse對象來構(gòu)建和管理的,它代表了即將發(fā)送給客戶端的HTTP響應(yīng)。

3.3.1.1. 狀態(tài)碼(Status Code)

每個HTTP響應(yīng)都會有一個狀態(tài)碼,用于表示請求的處理結(jié)果。在Spring WebFlux中,你可以通過如下方式設(shè)置狀態(tài)碼:

  • 使用靜態(tài)方法,如ServerResponse.ok()表示200 OK,ServerResponse.created()表示201 Created等。
  • 或者直接指定狀態(tài)碼,如ServerResponse.status(HttpStatus.NOT_FOUND)表示404 Not Found。

3.3.1.2. 響應(yīng)體(Body)

響應(yīng)體是響應(yīng)的主要內(nèi)容,可以是文本、JSON、XML等各種格式的數(shù)據(jù)。構(gòu)建響應(yīng)體的方式有多種:

  • 直接值:使用.bodyValue("響應(yīng)內(nèi)容"),適合于簡單的字符串或?qū)ο笾苯愚D(zhuǎn)換為響應(yīng)體。
  • 流(Stream).body(fromPublisher(publisher, MediaType)),當(dāng)響應(yīng)內(nèi)容來自一個publisher(如Flux或Mono)時,這種方式非常適合處理異步數(shù)據(jù)流。
  • 對象轉(zhuǎn)換:結(jié)合.body(toEntity(object)).bodyValue(object)結(jié)合Jackson等庫自動將Java對象轉(zhuǎn)換為JSON等格式的響應(yīng)體。

3.3.1.3. 內(nèi)容類型(Content-Type)

通過.contentType(MediaType)方法指定響應(yīng)的內(nèi)容類型,如MediaType.APPLICATION_JSON_UTF8表示JSON格式,MediaType.TEXT_PLAIN表示純文本等。

3.3.1.4. 頭部(Headers)

可以在響應(yīng)中添加自定義頭部信息,如.header("X-Custom-Header", "value")

3.3.1.5. 構(gòu)建響應(yīng)

一旦定義好狀態(tài)碼、響應(yīng)體、內(nèi)容類型和頭部等信息,通過.build()方法完成ServerResponse的構(gòu)建。這一步是必要的,它將之前設(shè)置的所有配置整合成一個待發(fā)送的響應(yīng)對象。

3.3.1.6.示例

假設(shè)我們要構(gòu)建一個返回JSON格式數(shù)據(jù)的響應(yīng):

import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

public Mono<ServerResponse> getUserInfo(ServerRequest request) {
    // 假設(shè)getUserDetails()是一個Mono<User>,User是自定義的Java對象
    Mono<User> userDetails = userService.getUserDetails(request.pathVariable("id"));

    return ServerResponse.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .body(userDetails, User.class) // 自動轉(zhuǎn)換User對象為JSON
            .switchIfEmpty(ServerResponse.notFound().build()); // 如果找不到用戶,返回404
}

在這個例子中,我們首先指定了響應(yīng)的狀態(tài)碼為200 OK,內(nèi)容類型為JSON,然后將從數(shù)據(jù)庫查詢到的用戶詳情(User對象)轉(zhuǎn)換為JSON響應(yīng)體。

如果查詢結(jié)果為空(即用戶不存在),則通過.switchIfEmpty()方法切換到返回404 Not Found的響應(yīng)。

3.3.2.響應(yīng)式類型

在響應(yīng)式編程領(lǐng)域,尤其是在使用Spring WebFlux和Reactor框架時,MonoFlux是兩個核心的響應(yīng)式類型,它們都是Reactor庫提供的對Reactive Streams規(guī)范的實現(xiàn)。這兩種類型都實現(xiàn)了Publisher接口,這意味著它們可以作為異步數(shù)據(jù)流的生產(chǎn)者,在響應(yīng)式系統(tǒng)中扮演著至關(guān)重要的角色。

3.3.2.1. Mono

  • 概念Mono代表0或1個元素的異步序列。換句話說,它要么發(fā)出一個元素,要么發(fā)出一個完成信號(表示沒有元素),或者發(fā)出一個錯誤信號。這使得它非常適合用于表示單個結(jié)果或者空結(jié)果的場景,比如數(shù)據(jù)庫查詢返回單行記錄,或者執(zhí)行一個可能會失敗的操作。
  • 典型用途:數(shù)據(jù)庫查詢的單一結(jié)果、網(wǎng)絡(luò)請求的響應(yīng)、計算單一值的操作等。
  • 操作符Mono提供了豐富的操作符,如mapflatMap、zipthen等,用于處理單個數(shù)據(jù)流的變換、組合和錯誤處理。

1. map

2. flatMap

3. then

4. zipWith

  • 功能:對Mono中的元素應(yīng)用一個函數(shù)進行轉(zhuǎn)換。
Mono<String> monoStr = Mono.just("Hello");
Mono<Integer> monoLength = monoStr.map(String::length);
  • 功能:將Mono中的元素轉(zhuǎn)換為另一個MonoFlux,然后扁平化這個結(jié)果流,使其成為單一流。
Mono<User> monoUser = ...; // 假設(shè)獲取用戶信息
Mono<Address> monoAddress = monoUser.flatMap(user -> getAddressForUser(user.getId()));
  • 功能:忽略Mono中的元素,僅當(dāng)前Mono完成時,執(zhí)行下一個Mono。
Mono<Void> saveUser = userRepository.save(newUser);
Mono<User> findUser = saveUser.then(userRepository.findById(newUser.getId()));
  • 功能:將兩個Mono的輸出按照某種方式結(jié)合(通常是元組),只有當(dāng)兩個Mono都成功時才會觸發(fā)。
Mono<User> monoUser = ...;
Mono<Order> monoOrder = ...;
Mono<Tuple2<User, Order>> combined = monoUser.zipWith(monoOrder);

3.3.2.2. Flux

  • 概念Flux代表0到N個元素的異步序列。它可以發(fā)出任意數(shù)量的元素,包括無限數(shù)量,直到它完成或者遇到錯誤。這使得Flux非常適合處理集合、事件流或者任何可能有多次數(shù)據(jù)推送的場景。
  • 典型用途:處理列表或集合的數(shù)據(jù)、實時數(shù)據(jù)流(如WebSocket消息)、數(shù)據(jù)庫查詢的多行結(jié)果等。
  • 操作符Flux同樣提供了豐富的操作符,如map、filterconcatMap、buffer、window等,用于處理數(shù)據(jù)流的變換、過濾、合并、窗口化等操作。

1. map

2. filter

3. flatMap

4. buffer

5. concatMap

  • 功能:對流中的每個元素應(yīng)用一個函數(shù)進行轉(zhuǎn)換。
Flux<String> names = Flux.fromIterable(Arrays.asList("Alice", "Bob", "Charlie"));
Flux<Integer> lengths = names.map(String::length);
  • 功能:根據(jù)條件從流中篩選元素。
Flux<String> names = ...;
Flux<String> longNames = names.filter(name -> name.length() > 5);
  • 功能:將流中的每個元素轉(zhuǎn)換為一個新的流,然后將這些流合并成一個單一的流。
Flux<User> users = ...;
Flux<Order> orders = users.flatMap(user -> getOrderListForUser(user.getId()));
  • 功能:將流中的元素收集到緩沖區(qū)中,達到一定條件(如數(shù)量或時間)后作為一個列表或數(shù)組發(fā)出。
Flux<Integer> numbers = Flux.interval(Duration.ofMillis(100)).take(10);
Flux<List<Integer>> buffered = numbers.buffer(3); // 每3個元素打包一次

功能:類似于flatMap,但保證按源流的順序依次處理每個元素產(chǎn)生的流。

Flux<User> users = ...;
Flux<Order> ordersSequential = users.concatMap(user -> getOrderListForUser(user.getId()));

3.3.3.3.共同特點

  • 響應(yīng)式MonoFlux都是非阻塞的,支持背壓(Backpressure),能夠在數(shù)據(jù)生產(chǎn)者和消費者之間自動調(diào)節(jié)數(shù)據(jù)流動的速度,防止生產(chǎn)過快導(dǎo)致消費端處理不過來的情況。
  • 鏈式操作:兩者都支持鏈式調(diào)用操作符,允許以聲明式的方式構(gòu)建復(fù)雜的異步數(shù)據(jù)處理流程,而無需顯式地管理線程或回調(diào)。
  • 異步和事件驅(qū)動:它們的設(shè)計哲學(xué)鼓勵編寫異步和事件驅(qū)動的代碼,提高了系統(tǒng)的可伸縮性和資源利用率。

3.3.3.4.轉(zhuǎn)換關(guān)系

MonoFlux之間可以通過操作符互相轉(zhuǎn)換,例如,Mono可以通過flatMapMany轉(zhuǎn)換為Flux,而多個MonoFlux可以通過concat、merge等操作合并。

4.WebClient

WebClient是Spring Framework 5引入的一個非阻塞、響應(yīng)式的HTTP客戶端,它屬于Spring WebFlux模塊的一部分。它設(shè)計用于構(gòu)建高性能、異步的Web服務(wù)客戶端,特別適合用于處理大量的并發(fā)請求和與響應(yīng)式服務(wù)交互。下面是對WebClient的詳細講解:

核心特性

  • 非阻塞WebClient基于非阻塞IO,這意味著它在等待數(shù)據(jù)時不會占用線程,從而能夠高效地處理大量并發(fā)連接,減少資源消耗。
  • 響應(yīng)式:遵循Reactor項目中的響應(yīng)式編程模型,使用MonoFlux作為返回類型,便于處理異步數(shù)據(jù)流和背壓機制。
  • 鏈式調(diào)用:提供了一套流暢的API,允許通過鏈式調(diào)用來構(gòu)建HTTP請求,易于理解和維護。
  • 配置靈活:支持多種配置選項,包括基礎(chǔ)URL、默認頭、超時設(shè)置、SSL配置等。
  • 內(nèi)容協(xié)商:自動處理內(nèi)容類型和編碼,支持JSON、XML等多種數(shù)據(jù)格式的序列化和反序列化。
  • 過濾器:允許添加自定義過濾器來修改請求或響應(yīng),實現(xiàn)日志記錄、認證、重試邏輯等。

基本使用

  • 創(chuàng)建WebClient實例
// 最簡單的創(chuàng)建方式
WebClient client = WebClient.create();

// 配置化創(chuàng)建
WebClient client = WebClient.builder()
        .baseUrl("http://example.com")
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .build();
  • 發(fā)送GET請求
Mono<String> response = client.get()
        .uri("/api/data")
        .retrieve() // 獲取響應(yīng)體
        .bodyToMono(String.class); // 將響應(yīng)體轉(zhuǎn)換為String類型
  • 發(fā)送POST請求
Mono<String> response = client.post()
        .uri("/api/data")
        .body(BodyInserters.fromObject(someObject)) // 發(fā)送對象
        .retrieve()
        .bodyToMono(String.class);

處理響應(yīng)

  • 使用.block()方法會阻塞當(dāng)前線程直到響應(yīng)到來,這通常只在測試或者非響應(yīng)式環(huán)境中使用。
  • 在響應(yīng)式環(huán)境中,應(yīng)該通過訂閱MonoFlux來處理響應(yīng),或者將其與其它響應(yīng)式流進行組合。

錯誤處理

可以使用.onErrorResume等操作符來優(yōu)雅地處理錯誤情況,例如重試邏輯或返回默認值。

Mono<String> withErrorHandling = client.get()
        .uri("/api/data")
        .retrieve()
        .onErrorResume(WebClientResponseException.class, e -> {
            // 處理錯誤,比如返回默認值
            return Mono.just("Default Value");
        })
        .bodyToMono(String.class);

總結(jié)

WebClient是一個強大且靈活的工具,它在現(xiàn)代Web應(yīng)用和服務(wù)間通信中扮演著重要角色,特別是當(dāng)需要構(gòu)建高性能、可擴展的系統(tǒng)時。通過充分利用響應(yīng)式編程的優(yōu)勢,開發(fā)者可以構(gòu)建出更加高效、易維護的客戶端邏輯。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • java中哈希表及其應(yīng)用詳解

    java中哈希表及其應(yīng)用詳解

    Java中哈希表(Hashtable)是如何實現(xiàn)的呢?Hashtable中有一個內(nèi)部類Entry,用來保存單元數(shù)據(jù),我們用來構(gòu)建哈希表的每一個數(shù)據(jù)是Entry的一個實例。假設(shè)我們保存下面一組數(shù)據(jù),第一列作為key, 第二列作為value。
    2015-06-06
  • springboot?@PostConstruct無效的解決

    springboot?@PostConstruct無效的解決

    這篇文章主要介紹了springboot?@PostConstruct無效的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 最新評論