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

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

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

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

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

1.Spring WebFlux 簡(jiǎn)介

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

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

1.1.異步非阻塞

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

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

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

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

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

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

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

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

1.2.2.背壓管理

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

1.2.3.Project Reactor

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

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

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

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

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

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

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

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

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

1.3.函數(shù)式編程

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

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

1.3.1.請(qǐng)求路由

使用 RouterFunction 定義請(qǐng)求路由

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

路由謂語(yǔ)和處理器

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

1.3.2.函數(shù)式端點(diǎn)

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

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

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

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

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

2.1 環(huán)境準(zhǔn)備

項(xiàng)目依賴配置

基于Maven的Springboot項(xiàng)目,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來(lái)定義請(qǐng)求的路由。

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

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() 是用來(lái)創(chuàng)建路由規(guī)則的起點(diǎn)。
  • GET("/hello") 是來(lái)自RequestPredicates的靜態(tài)方法,定義了一個(gè)謂詞,用于匹配HTTP GET方法并且路徑為"/hello"的請(qǐng)求。
  • handler::hello 是一個(gè)方法引用,指向MyHandler類中名為hello的方法。這意味著當(dāng)匹配到上述HTTP請(qǐng)求條件時(shí),會(huì)調(diào)用handler.hello()方法來(lái)處理請(qǐng)求,并期待它返回一個(gè)ServerResponse對(duì)象作為響應(yīng)。
2.2.2.使用 HandlerFunction 處理請(qǐng)求

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

這些方法可以返回Mono<ServerResponse>Flux<ServerResponse>,這取決于它們處理的是單個(gè)響應(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請(qǐng)求并返回一個(gè)響應(yīng)。

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

提取查詢參數(shù):

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

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

  • ServerResponse.ok():創(chuàng)建一個(gè)表示成功(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)來(lái)包裝問(wèn)候消息字符串,表明響應(yīng)體是一個(gè)包含單個(gè)元素的異步序列,其類型為String。這確保了整個(gè)處理過(guò)程是非阻塞的。

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));
    }
}

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

@Order(Ordered.HIGHEST_PRECEDENCE)

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

handleException 方法

參數(shù)

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

功能

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

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

3.1.RouterFunction

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

下面詳細(xì)介紹其用法:

3.1.1. 基本概念

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

3.1.2. 創(chuàng)建 RouterFunction

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

以下是一個(gè)簡(jiǎn)單的示例,展示如何定義一個(gè)路由來(lái)處理 GET 請(qǐng)求:

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!"));
    }
}

這段代碼定義了一個(gè)簡(jiǎn)單的RouterFunction<ServerResponse>實(shí)例,用于處理一個(gè)HTTP GET請(qǐng)求到/hello路徑的路由邏輯。

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

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

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

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

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

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

3.1.3. 注冊(cè) RouterFunction

要在 Spring Boot 應(yīng)用中注冊(cè) RouterFunction,通常在配置類中聲明為 @Bean,以便 Spring 自動(dòng)發(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 是一個(gè)包含業(yè)務(wù)邏輯的 HandlerFunction。

3.1.4. 組合 RouterFunction

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

  • 謂詞組合:可以使用 and, or 等邏輯運(yùn)算符組合謂詞,以實(shí)現(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);
    }
    // 處理簡(jiǎn)單的文本請(qǐng)求
    public Mono<ServerResponse> haha(ServerRequest request) {
        return ServerResponse.ok()
                .contentType(MediaType.TEXT_PLAIN)
                .bodyValue("haha!");
    }

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

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

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

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

3.1.6. 錯(cuò)誤處理

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

3.1.6.1.自帶異常處理

    @Bean
    public RouterFunction<ServerResponse> route(MyHandler handler) {
        return RouterFunctions.route()
                .GET("/greeting", handler::reactorExample)
                // 全局錯(cuò)誤處理
                .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.全局異常處理

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

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

3.1.7. 高級(jí)用法

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

3.2.請(qǐng)求接參

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

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

3.2.1. 路徑變量(Path Variables)

在路由定義中,你可以使用 {variableName} 來(lái)標(biāo)記路徑變量,然后在處理器方法中通過(guò) @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. 請(qǐng)求體(Request Body)

對(duì)于POST、PUT等方法,你可能需要從請(qǐng)求體中讀取數(shù)據(jù)。可以使用 @RequestBody 注解,并指定相應(yīng)的對(duì)象類型來(lái)自動(dòng)綁定JSON或表單數(shù)據(jù)。

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

3.2.4. 請(qǐng)求頭(Request Headers)

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

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

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

3.3.1.ServerResponse對(duì)象

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

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

每個(gè)HTTP響應(yīng)都會(huì)有一個(gè)狀態(tài)碼,用于表示請(qǐng)求的處理結(jié)果。在Spring WebFlux中,你可以通過(guò)如下方式設(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)容"),適合于簡(jiǎn)單的字符串或?qū)ο笾苯愚D(zhuǎn)換為響應(yīng)體。
  • 流(Stream).body(fromPublisher(publisher, MediaType)),當(dāng)響應(yīng)內(nèi)容來(lái)自一個(gè)publisher(如Flux或Mono)時(shí),這種方式非常適合處理異步數(shù)據(jù)流。
  • 對(duì)象轉(zhuǎn)換:結(jié)合.body(toEntity(object)).bodyValue(object)結(jié)合Jackson等庫(kù)自動(dòng)將Java對(duì)象轉(zhuǎn)換為JSON等格式的響應(yīng)體。

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

通過(guò).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)容類型和頭部等信息,通過(guò).build()方法完成ServerResponse的構(gòu)建。這一步是必要的,它將之前設(shè)置的所有配置整合成一個(gè)待發(fā)送的響應(yīng)對(duì)象。

3.3.1.6.示例

假設(shè)我們要構(gòu)建一個(gè)返回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()是一個(gè)Mono<User>,User是自定義的Java對(duì)象
    Mono<User> userDetails = userService.getUserDetails(request.pathVariable("id"));

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

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

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

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

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

3.3.2.1. Mono

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

1. map

2. flatMap

3. then

4. zipWith

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

3.3.2.2. Flux

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

1. map

2. filter

3. flatMap

4. buffer

5. concatMap

  • 功能:對(duì)流中的每個(gè)元素應(yīng)用一個(gè)函數(shù)進(jìn)行轉(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);
  • 功能:將流中的每個(gè)元素轉(zhuǎn)換為一個(gè)新的流,然后將這些流合并成一個(gè)單一的流。
Flux<User> users = ...;
Flux<Order> orders = users.flatMap(user -> getOrderListForUser(user.getId()));
  • 功能:將流中的元素收集到緩沖區(qū)中,達(dá)到一定條件(如數(shù)量或時(shí)間)后作為一個(gè)列表或數(shù)組發(fā)出。
Flux<Integer> numbers = Flux.interval(Duration.ofMillis(100)).take(10);
Flux<List<Integer>> buffered = numbers.buffer(3); // 每3個(gè)元素打包一次

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

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

3.3.3.3.共同特點(diǎn)

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

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

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

4.WebClient

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

核心特性

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

基本使用

  • 創(chuàng)建WebClient實(shí)例
// 最簡(jiǎn)單的創(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請(qǐng)求
Mono<String> response = client.get()
        .uri("/api/data")
        .retrieve() // 獲取響應(yīng)體
        .bodyToMono(String.class); // 將響應(yīng)體轉(zhuǎn)換為String類型
  • 發(fā)送POST請(qǐng)求
Mono<String> response = client.post()
        .uri("/api/data")
        .body(BodyInserters.fromObject(someObject)) // 發(fā)送對(duì)象
        .retrieve()
        .bodyToMono(String.class);

處理響應(yīng)

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

錯(cuò)誤處理

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

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

總結(jié)

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

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

相關(guān)文章

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

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

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

    springboot?@PostConstruct無(wú)效的解決

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