Spring WebFlux之響應(yīng)式編程詳解
在軟件開發(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!"));
route
是RouterFunctions
類中的一個(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í),Mono
和Flux
是兩個(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
、zip
、then
等,用于處理單個(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è)Mono
或Flux
,然后扁平化這個(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
、filter
、concatMap
、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)式:
Mono
和Flux
都是非阻塞的,支持背壓(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)系
Mono
和Flux
之間可以通過(guò)操作符互相轉(zhuǎn)換,例如,Mono
可以通過(guò)flatMapMany
轉(zhuǎn)換為Flux
,而多個(gè)Mono
或Flux
可以通過(guò)concat
、merge
等操作合并。
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)式編程模型,使用
Mono
和Flux
作為返回類型,便于處理異步數(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ò)訂閱
Mono
或Flux
來(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)文章
SpringBoot配置主從數(shù)據(jù)庫(kù)實(shí)現(xiàn)讀寫分離
現(xiàn)在的 Web 應(yīng)用大都是讀多寫少,本文主要介紹了SpringBoot配置主從數(shù)據(jù)庫(kù)實(shí)現(xiàn)讀寫分離,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11SpringMVC加載控制與Postmand的使用和Rest風(fēng)格的引入及RestFul開發(fā)全面詳解
SpringMVC是一種基于Java,實(shí)現(xiàn)了Web MVC設(shè)計(jì)模式,請(qǐng)求驅(qū)動(dòng)類型的輕量級(jí)Web框架,即使用了MVC架構(gòu)模式的思想,將Web層進(jìn)行職責(zé)解耦?;谡?qǐng)求驅(qū)動(dòng)指的就是使用請(qǐng)求-響應(yīng)模型,框架的目的就是幫助我們簡(jiǎn)化開發(fā),SpringMVC也是要簡(jiǎn)化我們?nèi)粘eb開發(fā)2022-10-10解決項(xiàng)目沒(méi)有build path的問(wèn)題
這篇文章主要介紹了解決項(xiàng)目沒(méi)有build path的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01Java注解機(jī)制之Spring自動(dòng)裝配實(shí)現(xiàn)原理詳解
這篇文章主要為大家詳細(xì)介紹了Java注解機(jī)制之Spring自動(dòng)裝配實(shí)現(xiàn)原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10淺析Java中print、printf、println的區(qū)別
以下是對(duì)Java中print、printf、println的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下2013-08-08MyBatis使用嵌套查詢collection和association的實(shí)現(xiàn)
本文詳細(xì)介紹了使用MyBatis框架進(jìn)行數(shù)據(jù)庫(kù)操作時(shí),如何利用collection標(biāo)簽實(shí)現(xiàn)一對(duì)多的嵌套查詢和使用association標(biāo)簽實(shí)現(xiàn)一對(duì)一的嵌套查詢,感興趣的可以了解一下2024-09-09

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