Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
pring 5.0 中發(fā)布了重量級(jí)組件 Webflux,拉起了響應(yīng)式編程的規(guī)模使用序幕。
WebFlux 使用的場(chǎng)景是異步非阻塞的,使用 Webflux 作為系統(tǒng)解決方案,在大多數(shù)場(chǎng)景下可以提高系統(tǒng)吞吐量。Spring Boot 2.0 是基于 Spring5 構(gòu)建而成,因此 Spring Boot 2.X 將自動(dòng)繼承了 Webflux 組件,本篇給大家介紹如何在 Spring Boot 中使用 Webflux 。
為了方便大家理解,我們先來(lái)了解幾個(gè)概念。
響應(yīng)式編程
在計(jì)算機(jī)中,響應(yīng)式編程或反應(yīng)式編程(英語(yǔ):Reactive programming)是一種面向數(shù)據(jù)流和變化傳播的編程范式。這意味著可以在編程語(yǔ)言中很方便地表達(dá)靜態(tài)或動(dòng)態(tài)的數(shù)據(jù)流,而相關(guān)的計(jì)算模型會(huì)自動(dòng)將變化的值通過(guò)數(shù)據(jù)流進(jìn)行傳播。
例如,在命令式編程環(huán)境中,a=b+c 表示將表達(dá)式的結(jié)果賦給 a,而之后改變 b 或 c 的值不會(huì)影響 a 。但在響應(yīng)式編程中,a 的值會(huì)隨著 b 或 c 的更新而更新。
響應(yīng)式編程是基于異步和事件驅(qū)動(dòng)的非阻塞程序,只需要在程序內(nèi)啟動(dòng)少量線程擴(kuò)展,而不是水平通過(guò)集群擴(kuò)展。
用大白話講,我們以前編寫(xiě)的大部分都是阻塞類(lèi)的程序,當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)時(shí)任務(wù)會(huì)被阻塞,直到這個(gè)任務(wù)完成后再返回給前端;響應(yīng)式編程接到請(qǐng)求后只是提交了一個(gè)請(qǐng)求給后端,后端會(huì)再安排另外的線程去執(zhí)行任務(wù),當(dāng)任務(wù)執(zhí)行完成后再異步通知到前端。
Reactor
Java 領(lǐng)域的響應(yīng)式編程庫(kù)中,最有名的算是 Reactor 了。Reactor 也是 Spring 5 中反應(yīng)式編程的基礎(chǔ),Webflux 依賴 Reactor 而構(gòu)建。
Reactor 是一個(gè)基于 JVM 之上的異步應(yīng)用基礎(chǔ)庫(kù)。為 Java 、Groovy 和其他 JVM 語(yǔ)言提供了構(gòu)建基于事件和數(shù)據(jù)驅(qū)動(dòng)應(yīng)用的抽象庫(kù)。Reactor 性能相當(dāng)高,在最新的硬件平臺(tái)上,使用無(wú)堵塞分發(fā)器每秒鐘可處理 1500 萬(wàn)事件。
簡(jiǎn)單說(shuō),Reactor 是一個(gè)輕量級(jí) JVM 基礎(chǔ)庫(kù),幫助你的服務(wù)或應(yīng)用高效,異步地傳遞消息。Reactor 中有兩個(gè)非常重要的概念 Flux 和 Mono 。
Flux 和 Mono
Flux 和 Mono 是 Reactor 中的兩個(gè)基本概念。Flux 表示的是包含 0 到 N 個(gè)元素的異步序列。在該序列中可以包含三種不同類(lèi)型的消息通知:正常的包含元素的消息、序列結(jié)束的消息和序列出錯(cuò)的消息。當(dāng)消息通知產(chǎn)生時(shí),訂閱者中對(duì)應(yīng)的方法 onNext(), onComplete()和 onError()會(huì)被調(diào)用。
Mono 表示的是包含 0 或者 1 個(gè)元素的異步序列。該序列中同樣可以包含與 Flux 相同的三種類(lèi)型的消息通知。Flux 和 Mono 之間可以進(jìn)行轉(zhuǎn)換。對(duì)一個(gè) Flux 序列進(jìn)行計(jì)數(shù)操作,得到的結(jié)果是一個(gè) Mono對(duì)象。把兩個(gè) Mono 序列合并在一起,得到的是一個(gè) Flux 對(duì)象。
WebFlux 是什么?
WebFlux 模塊的名稱是 spring-webflux,名稱中的 Flux 來(lái)源于 Reactor 中的類(lèi) Flux。Spring webflux 有一個(gè)全新的非堵塞的函數(shù)式 Reactive Web 框架,可以用來(lái)構(gòu)建異步的、非堵塞的、事件驅(qū)動(dòng)的服務(wù),在伸縮性方面表現(xiàn)非常好。
非阻塞的關(guān)鍵預(yù)期好處是能夠以小的固定數(shù)量的線程和較少的內(nèi)存進(jìn)行擴(kuò)展。在服務(wù)器端 WebFlux 支持2種不同的編程模型:
WebFlux 模塊從上到下依次是 Router Functions、WebFlux、Reactive Streams 三個(gè)新組件。
默認(rèn)情況下,Spring Boot 2 使用 Netty WebFlux,因?yàn)?Netty 在異步非阻塞空間中被廣泛使用,異步非阻塞連接可以節(jié)省更多的資源,提供更高的響應(yīng)度。通過(guò)比較 Servlet 3.1 非阻塞 I / O 沒(méi)有太多的使用,因?yàn)槭褂盟某杀颈容^高,Spring WebFlux 打開(kāi)了一條實(shí)用的通路。
值得注意的是:支持 reactive 編程的數(shù)據(jù)庫(kù)只有 MongoDB, redis, Cassandra, Couchbase
Spring Webflux
Spring Boot 2.0 包括一個(gè)新的 spring-webflux 模塊。該模塊包含對(duì)響應(yīng)式 HTTP 和 WebSocket 客戶端的支持,以及對(duì) REST,HTML 和 WebSocket 交互等程序的支持。一般來(lái)說(shuō),Spring MVC 用于同步處理,Spring Webflux 用于異步處理。
Spring Boot Webflux 有兩種編程模型實(shí)現(xiàn),一種類(lèi)似 Spring MVC 注解方式,另一種是基于 Reactor 的響應(yīng)式方式。
快速上手
添加 webflux 依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
通過(guò) IEDA 的依賴關(guān)系圖我們可以返現(xiàn)spring-boot-starter-webflux依賴于spring-webflux、Reactor 和 Netty 相關(guān)依賴包。
創(chuàng)建 Controller
@RestController public class HelloController { @GetMapping("/hello") public Mono<String> hello() { return Mono.just("Welcome to reactive world ~"); } }
通過(guò)上面的示例可以發(fā)現(xiàn),開(kāi)發(fā)模式和之前 Spring Mvc 的模式差別不是很大,只是在方法的返回值上有所區(qū)別。
測(cè)試類(lèi)
@RunWith(SpringRunner.class) @WebFluxTest(controllers=HelloController.class) public class HelloTests { @Autowired WebTestClient client; @Test public void getHello() { client.get().uri("/hello").exchange().expectStatus().isOk(); } }
運(yùn)行測(cè)試類(lèi),測(cè)試用例通過(guò)表示服務(wù)正常。啟動(dòng)項(xiàng)目后,訪問(wèn)地址:http://localhost:8080/hello,頁(yè)面返回信息:
Welcome to reactive world ~
證明 Webflux 集成成功。
以上便是 Spring Boot 集成 Webflux 最簡(jiǎn)單的 Demo ,后續(xù)我們繼續(xù)研究 Webflux 的使用。
關(guān)注我:私信回復(fù)“555”獲取往期Java高級(jí)架構(gòu)資料、源碼、筆記、視頻Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并發(fā)等架構(gòu)技術(shù)往期架構(gòu)視頻截圖
鏈路追蹤是可觀測(cè)性軟件系統(tǒng)的一個(gè)非常好的工具。它使開(kāi)發(fā)人員能夠了解應(yīng)用程序中和應(yīng)用程序之間不同交互發(fā)生的時(shí)間、地點(diǎn)和方式。同時(shí)讓觀測(cè)復(fù)雜的軟件系統(tǒng)變得更加容易。
從Spring Boot 3開(kāi)始,Spring Boot 中用于鏈路追蹤的舊 Spring Cloud Sleuth 解決方案將替換為新的 Micrometer Tracing 庫(kù)。
您可能已經(jīng)了解 Micrometer,因?yàn)樗郧氨挥米鞴_(kāi)獨(dú)立于平臺(tái)的指標(biāo)和監(jiān)控基于 JVM 的微服務(wù)(例如 Prometheus )的默認(rèn)解決方案。最新產(chǎn)品通過(guò)獨(dú)立于平臺(tái)的鏈路追蹤解決方案擴(kuò)展了 Micrometer 生態(tài)系統(tǒng)。這使得開(kāi)發(fā)人員能夠使用一個(gè)通用 API 來(lái)檢測(cè)其應(yīng)用程序,并以不同的格式將其導(dǎo)出到 Jaeger、Zipkin 或 OpenTelemetry 等鏈路追蹤收集器。
本文將介紹在響應(yīng)式編程 Kotlin 中,如何在 Spring Boot 3 WebFlux 利用 Micrometer 進(jìn)行鏈路追蹤。
接下來(lái),我們將創(chuàng)建一個(gè)簡(jiǎn)單的 Spring Boot 微服務(wù),它提供一個(gè)響應(yīng)式 REST 端點(diǎn),該端點(diǎn)在內(nèi)部查詢另一個(gè)第三方服務(wù)以獲取一些信息。目標(biāo)是導(dǎo)出兩個(gè)操作的 trace。
我們將從以下 Spring Boot Initializr 項(xiàng)目開(kāi)始,您可以在此處找到該項(xiàng)目。它包括帶有Kotlin Gradle DSL的Spring Boot 3.0.1、Spring Web Reactive (WebFlux)和帶有Prometheus的Spring Actuator。以下代碼主要使用Kotlin,但如果使用 Java 也是可以的,大多數(shù)方法都是相同的。
Spring 初始化模板(https://start.spring.io/) 帶有 Webflux、Spring Actuator 和 Prometheus 的 Spring Boot 3 Kotlin 模板
我們將首先添加一個(gè)帶有測(cè)試 endpoint 的簡(jiǎn)單 REST 控制器類(lèi),該測(cè)試 endpoint 使用 Spring WebClient 調(diào)用外部 API 。我們正在使用 suspend 關(guān)鍵字來(lái)使用Kotlin的協(xié)程。這使我們能夠在利用 Spring WebFlux 的響應(yīng)式流的同時(shí)編寫(xiě)命令式代碼。
在以下示例中,我們使用 Spring WebClient 調(diào)用外部 TODO-API,該 API 以 JSON 字符串形式返回 TODO 項(xiàng)。我們還將創(chuàng)建一條日志消息,其中稍后應(yīng)包含一些鏈路追蹤信息。
@RestController
class Controller {
val log=LoggerFactory.getLogger(javaClass)
val webClient=WebClient.builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.build()
@GetMapping("/test")
suspend fun test(): String {
// simulate some complex calculation
delay(1.seconds)
log.info("test log with tracing info")
// make web client call to external API
val externalTodos=webClient.get()
.uri("/todos/1")
.retrieve()
.bodyToMono(String::class.java)
.awaitSingle()
return externalTodos
}
}
在下一步中,我們將把 Micrometer tracing 依賴項(xiàng)添加到我們的build.gradle.kts文件中。由于 Micrometer 支持不同的鏈路追蹤格式和供應(yīng)商,因此依賴項(xiàng)被分開(kāi),我們只導(dǎo)入我們需要的內(nèi)容。為了保持所有依賴項(xiàng)同步,我們使用 Micrometer Tracing BOM(bom 清單)。此外,我們添加了核心依賴項(xiàng)和橋接器,以將 Micrometer Tracing 轉(zhuǎn)換為 OpenTelemetry 格式(其他格式也可用)。
implementation(platform("io.micrometer:micrometer-tracing-bom:1.0.0"))
implementation("io.micrometer:micrometer-tracing")
implementation("io.micrometer:micrometer-tracing-bridge-otel")
我們還需要添加導(dǎo)出器依賴項(xiàng)來(lái)導(dǎo)出創(chuàng)建的 trace。在此示例中,我們將使用由 OpenTelemetry 維護(hù)并由 Micrometer Tracing 支持的 Zipkin 導(dǎo)出器。
implementation("io.opentelemetry:opentelemetry-exporter-zipkin")
配置是設(shè)置鏈路追蹤必不可少的一步,配置文件application.yaml位于src/main/resources目錄下。
management:
tracing:
enabled: true
sampling.probability: 1.0
zipkin.tracing.endpoint: http://localhost:9411/api/v2/spans
logging.pattern.level: "trace_id=%mdc{traceId} span_id=%mdc{spanId} trace_flags=%mdc{traceFlags} %p"
現(xiàn)在我們已經(jīng)完成了服務(wù)設(shè)置,我們可以運(yùn)行它了。如果啟動(dòng)應(yīng)用程序,默認(rèn)情況下,服務(wù)器應(yīng)在端口下啟動(dòng) 8080。然后,可以通過(guò)打開(kāi)瀏覽器來(lái)調(diào)用我們創(chuàng)建的端點(diǎn)http://localhost:8080/test。以下是請(qǐng)求響應(yīng)內(nèi)容:
{ "userId" : 1 , "id" : 1 , "title" : "delectus aut autem" , "已完成" : false }
要查看調(diào)用端點(diǎn)時(shí)創(chuàng)建的實(shí)際鏈路追蹤,我們需要收集并查看它們。在本教程中,我們將使用zipkin導(dǎo)出器將數(shù)據(jù)導(dǎo)出到 觀測(cè)云。當(dāng)然也可以使用其他系統(tǒng),例如 Zipkin、Grafana Loki 或 Datadog。
現(xiàn)在您可以再次調(diào)用我們的 Spring Boot 服務(wù)的端點(diǎn)。之后,當(dāng)您在 觀測(cè)云 中搜索任何 tracing 時(shí),您應(yīng)該能夠找到端點(diǎn)請(qǐng)求的鏈路追蹤信息。
乍一看,一切似乎都運(yùn)行良好。然而,我們有兩個(gè)問(wèn)題。
解決了部分 issue 問(wèn)題,這些問(wèn)題可以在 Micrometer Tracing 文檔中找到,參考鏈接:https://micrometer.io/docs/observation#instrumentation_of_reactive_libraries_after_reactor_3_5_3。
如果我們查看應(yīng)用程序日志,可以發(fā)現(xiàn)調(diào)用端點(diǎn)時(shí)發(fā)出的日志消息。
trace_id=span_id=trace_flags=INFO 43636 --- [DefaultExecutor] com.example.tracing.Controller : test log with tracing info
正如你所看到的,trace_id 和 span_id沒(méi)有設(shè)置。這是因?yàn)?span style="color: #F0506E; --tt-darkmode-color: #F0506E;">Micrometer Tracing還無(wú)法輕松處理響應(yīng)式流中的鏈路追蹤上下文。此外,響應(yīng)式流的Kotlin協(xié)程包裝器隱藏了鏈路追蹤上下文。因此,我們必須推遲當(dāng)前響應(yīng)式流的上下文來(lái)獲取鏈路追蹤信息。實(shí)際上,這看起來(lái)如下所示:
Mono.deferContextual { contextView ->
ContextSnapshot.setThreadLocalsFrom(
contextView,
ObservationThreadLocalAccessor.KEY
).use {
log.info("test log with tracing info")
Mono.empty<String>()
}
}.awaitSingleOrNull()
為了更符合應(yīng)用性,我們可以將示例代碼提取到一個(gè)單獨(dú)的函數(shù)中。
@GetMapping("/test")
suspend fun test(): String {
// ...
observeCtx { log.info("test log with tracing info") }
// ...
}
suspend inline fun observeCtx(crossinline f: () -> Unit) {
Mono.deferContextual { contextView ->
ContextSnapshot.setThreadLocalsFrom(
contextView,
ObservationThreadLocalAccessor.KEY
).use {
f()
Mono.empty<Unit>()
}
}.awaitSingleOrNull()
}
如果我們現(xiàn)在啟動(dòng)應(yīng)用程序并調(diào)用我們的端點(diǎn),我們應(yīng)該能夠trace_id在日志中看到。
trace_id=6c0053eba01199f194f5f76ff8d61917 span_id=967d591266756905 trace_flags=INFO 45139 --- [DefaultExecutor] com.example.tracing.Controller : test log with tracing info
第二個(gè)問(wèn)題可以通過(guò)查看觀測(cè)云中的 trace 來(lái)發(fā)現(xiàn)。它僅顯示端點(diǎn)的父鏈路追蹤,但不顯示調(diào)用的子范圍 WebClient。理論上,Spring WebClient 以及 RestTemplate 都是由 Micrometer 自動(dòng)檢測(cè)的。但是如果我們查看代碼,就會(huì)發(fā)現(xiàn)我們正在使用靜態(tài)構(gòu)建器方法 WebClient。為了從 WebClient 獲取自動(dòng)鏈路追蹤,我們需要使用 Spring 框架提供的構(gòu)建器 bean。它可以通過(guò)我們類(lèi)的構(gòu)造函數(shù)注入Controller。
@RestController
class Controller(
webClientBuilder: WebClient.Builder
) {
val webClient=webClientBuilder // use injected builder
.baseUrl("https://jsonplaceholder.typicode.com")
.build()
// ...
}
通過(guò)上面的代碼調(diào)整后重新調(diào)用 endpoint,我們?cè)?/span>觀測(cè)云中可以看到WebClient的跨度。Micrometer Tracing 還將自動(dòng)為包含trace_id. 例如,如果我們調(diào)用另一個(gè)帶有鏈路追蹤功能的微服務(wù),它可以獲取 ID 并向觀測(cè)云發(fā)送附加信息。
Micrometer Tracing 在 Spring 中自動(dòng)為我們做了很多事情。但是,有時(shí)我們可能希望向鏈路追蹤范圍添加特定信息或觀察應(yīng)用程序中非傳入或傳出調(diào)用的特定部分。
我們可以定義自定義標(biāo)簽并將其添加到當(dāng)前觀察中以增強(qiáng)鏈路追蹤數(shù)據(jù)。要檢索當(dāng)前鏈路追蹤,我們可以使用ObservationRegistry類(lèi)的 bean 。與日志記錄問(wèn)題類(lèi)似,我們必須使用包裝函數(shù)來(lái)獲取正確的上下文。
@GetMapping("/test")
suspend fun test(): String {
observeCtx {
val currentObservation=observationRegistry.currentObservation
currentObservation?.highCardinalityKeyValue("test_key", "test sample value")
}
// ...
}
添加此代碼后,我們可以在觀測(cè)云中看到我們的自定義標(biāo)簽及其值。
使用 Micrometer API 創(chuàng)建自定義可觀測(cè)(跨度)通常很容易。但是,在使用響應(yīng)式流和協(xié)程時(shí),我們需要幫助上下文鏈路追蹤。如果我們?cè)诙它c(diǎn)處理程序中創(chuàng)建一個(gè)新的觀測(cè),它將被視為一個(gè)單獨(dú)的鏈路追蹤。為了使代碼可重用,我們可以編寫(xiě)一個(gè)簡(jiǎn)單的包裝函數(shù)來(lái)創(chuàng)建新的觀測(cè)點(diǎn)。它的工作原理與我們之前創(chuàng)建的用于使用 trace_id 。
suspend fun runObserved(
name: String,
observationRegistry: ObservationRegistry,
f: suspend () -> Unit
) {
Mono.deferContextual { contextView ->
ContextSnapshot.setThreadLocalsFrom(
contextView,
ObservationThreadLocalAccessor.KEY
).use {
val observation=Observation.start(name, observationRegistry)
Mono.just(observation).flatMap {
mono { f() }
}.doOnError {
observation.error(it)
observation.stop()
}.doOnSuccess {
observation.stop()
}
}
}.awaitSingleOrNull()
}
該函數(shù)可以將任何掛起函數(shù)包裝在新的觀察周?chē)?。一旦?zhí)行了給定的函數(shù),它將自動(dòng)停止觀測(cè)。此外,我們將追蹤可能發(fā)生的任何錯(cuò)誤并將其附加到鏈路追蹤中。
我們現(xiàn)在可以應(yīng)用這個(gè)函數(shù)來(lái)觀察任何代碼,例如函數(shù)的執(zhí)行delay。
@GetMapping("/test")
suspend fun test(): String {
runObserved("delay", observationRegistry) {
delay(1.seconds)
}
// ....
}
將此代碼添加到端點(diǎn)處理程序后,觀測(cè)云將向我們顯示該操作的自定義范圍。
典型的 Spring Boot 應(yīng)用程序通常會(huì)連接到實(shí)際應(yīng)用程序中的數(shù)據(jù)庫(kù)。要利用響應(yīng)式技術(shù)棧,建議使用 R2DBC(https://r2dbc.io/) API 而不是 JDBC 。
由于Micrometer Tracing是一項(xiàng)相當(dāng)新的技術(shù),目前還沒(méi)有可用的自動(dòng)追蹤。然而,Spring 團(tuán)隊(duì)正在研究創(chuàng)建自動(dòng)配置。實(shí)驗(yàn)存儲(chǔ)庫(kù)參考鏈接:https://github.com/spring-projects-experimental/r2dbc-micrometer-spring-boot。
當(dāng)前項(xiàng)目,需將添加以下依賴項(xiàng)到build.gradle.kts. 為了方便測(cè)試,我們不會(huì)使用真實(shí)的數(shù)據(jù)庫(kù),而是使用 H2 內(nèi)存數(shù)據(jù)庫(kù)(https://www.h2database.com/html/main.html)。
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
runtimeOnly("com.h2database:h2")
runtimeOnly("io.r2dbc:r2dbc-h2")
// R2DBC micrometer auto tracing
implementation("org.springframework.experimental:r2dbc-micrometer-spring-boot:1.0.2")
在Kotlin代碼中,添加了一個(gè)帶有協(xié)程支持的簡(jiǎn)單 CRUD 存儲(chǔ)庫(kù)。如下所示:
@Table("todo")
data class ToDo(
@Id
val id: Long=0,
val title: String,
)
interface ToDoRepository : CoroutineCrudRepository<ToDo, Long>
@RestController
class Controller(
val todoRepo: ToDoRepository,
// ...
) {
@GetMapping("/test")
suspend fun test(): String {
// ...
// save
val entry=ToDo(0,"Springboot3 + WebFlux + Kotlin ")
todoRepo.save(entry)
// Sample traced DB call
val dbtodos=todoRepo.findAll().toList()
// ...
return "${dbtodos.size} $externalTodos"
}
}
調(diào)用我們的 endpoint 將會(huì)再添加一個(gè)跨度。新的跨度名為query,包含多個(gè)標(biāo)簽,包括Spring Data R2DBC(https://spring.io/projects/spring-data-r2dbc/) 執(zhí)行的 SQL 查詢。
Micrometer 和新的鏈路追蹤擴(kuò)展統(tǒng)一了Spring Boot 3及以上版本的可觀測(cè)性技術(shù)棧。為不同公司及其技術(shù)棧使用的不同鏈路追蹤解決方案提供了很好的抽象。因此,它簡(jiǎn)化了我們開(kāi)發(fā)人員的工作。
在 Spring WebFlux 的響應(yīng)式編程方面,仍然有一些改進(jìn)的潛力,尤其是 Kotlin。Micrometer 團(tuán)隊(duì)正在與Project Reactor (https://projectreactor.io/),Spring WebFlux 使用的響應(yīng)式庫(kù)背后的團(tuán)隊(duì)進(jìn)行積極會(huì)談,以簡(jiǎn)化響應(yīng)式技術(shù)棧的 Micrometer Tracing 的使用。
開(kāi)發(fā)Web應(yīng)用程序時(shí),經(jīng)常需要進(jìn)行一些全局性的配置,例如添加攔截器、設(shè)置消息轉(zhuǎn)換器、配置跨域資源共享(CORS)等。在Spring WebFlux中,我們可以通過(guò)實(shí)現(xiàn)WebFluxConfigurer接口來(lái)進(jìn)行這些全局配置。本文將詳細(xì)介紹如何使用WebFluxConfigurer進(jìn)行全局配置,以及常見(jiàn)的配置項(xiàng)和實(shí)現(xiàn)方式。
WebFluxConfigurer是Spring WebFlux框架中用于配置WebFlux全局特性的接口,它提供了多個(gè)方法用于配置不同的功能。
public interface WebFluxConfigurer {
default void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {}
default void addFormatters(FormatterRegistry registry) {}
default void configureViewResolvers(ViewResolverRegistry registry) {}
default void addArgumentResolvers(ArgumentResolverConfigurer configurer) {}
default void addReturnValueHandlers(ReturnValueHandlerConfigurer configurer) {}
default void configureHandlerExceptionResolvers(HandlerExceptionResolverConfigurer configurer) {}
default void addInterceptors(InterceptorRegistry registry) {}
default void addCorsMappings(CorsRegistry registry) {}
default void configurePathMatching(PathMatchConfigurer configurer) {}
default void configureWebSocketTransport(WebSocketTransportConfigurer configurer) {}
default void configureWebSocketHandlerMapping(WebSocketHandlerMappingConfigurer configurer) {}
default void configureHttpMessageReader(ServerHttpMessageReaderConfigurer configurer) {}
default void configureHttpMessageWriter(ServerHttpMessageWriterConfigurer configurer) {}
default void configureClientHttpRequestFactory(ClientHttpConnectorConfigurer configurer) {}
default void addResourceHandlers(ResourceHandlerRegistry registry) {}
default void configureServerSentEvent(ServerSentEventHttpMessageWriterConfigurer configurer) {}
}
在上述接口中,每個(gè)方法對(duì)應(yīng)一個(gè)特定的全局配置功能。接下來(lái),我們將詳細(xì)介紹每個(gè)方法的作用和實(shí)現(xiàn)方式。
在WebFlux中,消息編解碼器(Message Codec)負(fù)責(zé)將請(qǐng)求和響應(yīng)的數(shù)據(jù)轉(zhuǎn)換為對(duì)象,并進(jìn)行序列化和反序列化。可以通過(guò)configureHttpMessageCodecs方法來(lái)配置消息編解碼器。
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.customCodecs().encoder(new MyEncoder());
configurer.customCodecs().decoder(new MyDecoder());
}
}
在上述示例中,我們通過(guò)configureHttpMessageCodecs方法添加了自定義的編碼器和解碼器,實(shí)現(xiàn)了對(duì)特定數(shù)據(jù)格式的處理。
格式化器(Formatter)用于將字符串類(lèi)型的數(shù)據(jù)轉(zhuǎn)換為特定類(lèi)型的對(duì)象,例如將字符串轉(zhuǎn)換為日期對(duì)象。可以通過(guò)addFormatters方法來(lái)配置格式化器。
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
}
}
在上述示例中,我們通過(guò)addFormatters方法添加了日期格式化器,將日期字符串轉(zhuǎn)換為yyyy-MM-dd格式的日期對(duì)象。
視圖解析器(View Resolver)用于將邏輯視圖名稱解析為實(shí)際的視圖對(duì)象,例如將Thymeleaf模板文件解析為HTML視圖??梢酝ㄟ^(guò)configureViewResolvers方法來(lái)配置視圖解析器。
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(new ThymeleafViewResolver());
}
}
在上述示例中,我們通過(guò)configureViewResolvers方法添加了Thymeleaf視圖解析器,用于解析Thymeleaf模板文件。
在WebFlux中,參數(shù)解析器(Argument Resolver)用于將請(qǐng)求中的參數(shù)解析為控制器方法的參數(shù),而返回值處理器(Return Value Handler)用于將控制器方法的返回值處理為響應(yīng)數(shù)據(jù)??梢酝ㄟ^(guò)addArgumentResolvers和addReturnValueHandlers方法來(lái)配置參數(shù)解析器和返回值處理器。
配置參數(shù)解析器
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void addArgumentResolvers(ArgumentResolverConfigurer configurer) {
configurer.addCustomResolver(new MyArgumentResolver());
}
}
在上述示例中,我們通過(guò)addArgumentResolvers方法添加了自定義的參數(shù)解析器MyArgumentResolver。
配置返回值處理器
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void addReturnValueHandlers(ReturnValueHandlerConfigurer configurer) {
configurer.addCustomHandler(new MyReturnValueHandler());
}
}
在上述示例中,我們通過(guò)addReturnValueHandlers方法添加了自定義的返回值處理器MyReturnValueHandler。
在Web應(yīng)用程序開(kāi)發(fā)中,異常處理器(Exception Resolver)用于處理控制器方法中拋出的異常,并返回適當(dāng)?shù)捻憫?yīng)。可以通過(guò)configureHandlerExceptionResolvers方法來(lái)配置異常處理器。
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void configureHandlerExceptionResolvers(HandlerExceptionResolverConfigurer configurer) {
configurer.addExceptionHandler(new MyExceptionHandler());
}
}
在上述示例中,我們通過(guò)configureHandlerExceptionResolvers方法添加了自定義的異常處理器MyExceptionHandler。
攔截器(Interceptor)用于在處理請(qǐng)求之前或之后執(zhí)行一些額外的邏輯,例如記錄請(qǐng)求日志、權(quán)限驗(yàn)證等??梢酝ㄟ^(guò)addInterceptors方法來(lái)配置攔截器。
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/api/**");
}
}
在上述示例中,我們通過(guò)addInterceptors方法添加了自定義的攔截器MyInterceptor,并指定了攔截路徑為/api/**。
跨域資源共享(Cross-Origin Resource Sharing,CORS)是一種用于解決跨域訪問(wèn)問(wèn)題的機(jī)制,可以通過(guò)addCorsMappings方法來(lái)配置CORS。
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
}
在上述示例中,我們通過(guò)addCorsMappings方法配置了跨域訪問(wèn)的規(guī)則,允許所有來(lái)源的請(qǐng)求訪問(wèn)/api/**路徑,并指定了允許的方法、是否允許攜帶憑證以及最大緩存時(shí)間。
路徑匹配規(guī)則(Path Matching)用于指定URL路徑與請(qǐng)求處理器的映射關(guān)系??梢酝ㄟ^(guò)configurePathMatching方法來(lái)配置路徑匹配規(guī)則。
@Configuration
public class WebConfig implements WebFluxConfigurer {
@Override
public void configurePathMatching(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false)
.setUseTrailingSlashMatch(false);
}
}
在上述示例中,我們通過(guò)configurePathMatching方法配置了路徑匹配規(guī)則,禁用了后綴模式匹配和尾部斜杠匹配。
通過(guò)本文的詳細(xì)介紹,讀者應(yīng)該對(duì)如何使用WebFluxConfigurer進(jìn)行全局配置有了更深入的了解。合理地進(jìn)行全局配置可以提高開(kāi)發(fā)效率,并確保應(yīng)用程序的穩(wěn)定性和可維護(hù)性。希望本文能夠幫助讀者更好地理解和應(yīng)用Spring WebFlux中的全局配置功能,為開(kāi)發(fā)高效、靈活的Web應(yīng)用程序提供參考和指導(dǎo)。
公眾號(hào):九極客
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。