環境:SpringBoot2.4.12
請先閱讀:
Spring WebFlux使用函數式程式設計之HandlerFunction(1)
Spring WebFlux使用函數式程式設計之RouterFunction(2)
Spring WebFlux請求處理流程
一文帶你徹底了解Spring WebFlux的工作原理 Reactor響應式程式設計(Flux、Mono)基本用法
概述
與Spring MVC類似,Spring WebFlux是圍繞前端控制器模式設計的,其中核心處理程式WebHandler 的實作DispatcherHandler為請求處理提供共享算法,而實際工作由可配置的委托元件執行。該模型非常靈活,支援多種工作流。
DispatcherHandler從Spring配置中發現所需的委托元件。它本身也被設計為bean,并實作ApplicationContextAware以通路它運作的上下文。如果DispatcherHandler是用webHandler的bean名稱聲明的,那麼WebHttpHandlerBuilder會發現它,它會将請求處理鍊組合在一起,如webHandler API中所述。
WebFlux應用程式中的Spring配置通常包含:
- bean名稱為webHandler的DispatcherHandler
- WebFilter和WebExceptionHandler
- DispatcherHandler特殊bean
- 其它
配置被提供給WebHttpHandlerBuilder以建構處理鍊,如下例所示:
public class HttpHandlerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
public static class AnnotationConfig {
@Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
// applicationContext方法中會收集容器中WebFilter和WebExceptionHandler
// build方法中建構了HttpWebHandlerAdapter(實作了HttpHandler),該對象中
// 包裝了WebFilter和WebExceptionHandler集合
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
WebFluxProperties properties = propsProvider.getIfAvailable();
if (properties != null && StringUtils.hasText(properties.getBasePath())) {
Map<String, HttpHandler> handlersMap = Collections.singletonMap(properties.getBasePath(), httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
return httpHandler;
}
}
}
特殊一樣的Bean
DispatcherHandler委托特殊bean處理請求并呈現适當的響應。所謂“特殊bean”,是指實作WebFlux架構規定的Spring管理的對象執行個體。一般都内置這些Bean,不過你可以自定義、擴充或替換它們的屬性。
- HandlerMapping
将請求映射到處理程式。映射基于一些标準,這些标準的細節因HandlerMapping實作的不同而不同——注釋控制器、簡單URL模式映射等等。如:@RequestMapping注解的Controller或RouterFunction類型的Bean他們都是由不同的HandlerMapping來處理。
- HandlerAdapter
幫助DispatcherHandler調用映射到請求的處理程式,而不管該處理程式實際是如何調用的。例如,調用帶注釋的控制器需要解析注釋。HandlerAdapter的主要目的是保護DispatcherHandler不受這些細節的影響。簡單說就是不同的HandlerAdapter處理由不同HandlerMapping傳回的不同的Handler對象,比如:RequestMappingHandlerMapping傳回的HandlerMethod,RouterFunctionMapping傳回的HandlerFunction。
- HandlerResultHandler
處理處理程式調用的結果并完成響應。
WebFlux配置
應用程式可以聲明處理請求所需的基礎bean(列在Web Handler API和DispatcherHandler下面)。但是,在大多數情況下,WebFlux配置是最好的起點。它聲明所需的bean,并提供更進階别的配置回調API來自定義它。
請求處理
DispatcherHandler處理請求的方式如下:
- 每個HandlerMapping被要求找到一個比對的處理程式,并使用第一個比對。
- 如果找到處理程式,則通過适當的HandlerAdapter運作它,它将從執行中傳回的值公開為HandlerResult。
- HandlerResult被提供給适當的HandlerResultHandler,以通過直接寫入響應或使用視圖進行渲染來完成處理。
結果處理
調用處理程式的傳回值通過HandlerAdapter被包裝為HandlerResult,以及一些附加的上下文,并傳遞給聲稱支援它的第一個HandlerResultHandler。下表列出可用的HandlerResultHandler實作,所有這些實作都在WebFlux Config中聲明:
- ResponseEntityResultHandler
傳回值:ResponseEntity, 通常來自@Controller執行個體。
- ServerResponseResultHandler
傳回值:ServerResponse,通常來自功能端點。
- ResponseBodyResultHandler
傳回值:處理來自@ResponseBody方法或@RestController類的傳回值。
- ViewResolutionResultHandler
傳回值:CharSequence、視圖、模型、映射、渲染或任何其他對象都被視為模型屬性。
異常處理
從HandlerAdapter傳回的HandlerResult可以基于某些特定于處理程式的機制公開用于錯誤處理的函數。在以下情況下調用此錯誤函數:
- 處理程式(例如,@Controller)調用失敗。
- 通過HandlerResultHandler處理處理程式傳回值失敗。
隻要錯誤信号發生在從處理程式傳回的響應類型産生任何資料項之前,error函數就可以更改響應(例如,更改為錯誤狀态)。
這就是如何支援@Controller類中的@ExceptionHandler方法。相比之下,Spring MVC中的支援也是建立在HandlerExceptionResolver上的。注意:在WebFlux中,不能使用@ControllerAdvice來處理在選擇處理程式之前發生的異常。
@RestControllerAdvice
public class PackControllerAdvice {
@ExceptionHandler
public ResponseEntity<String> handle(Exception ex) {
ex.printStackTrace();
return ResponseEntity.ok(ex.getMessage() + ", Advice");
}
}
注意:這個不能處理調用處理程式之前的任何異常,處理程式之前的異常應該由WebExceptionHandler來處理
下面的異常處理句柄将會處理,由WebFilter執行個體鍊和目标WebHandle的異常。
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomWebExceptionHandler implements WebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
System.out.println("異常了: " + ex.getMessage()) ;
// 将錯誤傳遞下去,後面的onErrorResume還可以繼續執行;如果傳遞,那麼下一個處理器将會是DefaultErrorWebExceptionHandler
// return Mono.error(ex) ;
// exchange.getResponse()
// return Mono.error(ex) ;
// 下面不傳遞異常了,直接輸出錯誤資訊
ServerHttpResponse response = exchange.getResponse() ;
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR) ;
response.getHeaders().add("ContentType", "text/html;charset=utf8");
return response.writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap("ERROR".getBytes(Charset.forName("UTF-8"))))) ;
}
}
完畢!!!
Spring中Aware接口實作原了解析
Spring Cloud Nacos 開啟權限驗證
Spring Cloud Nacos配置中心實作原理
Spring IOC容器對Bean執行個體化的過程詳解源碼分析
使用Spring Boot Admin實時監控你的系統
Spring注解@Qualifier這種用法你知道嗎?
Spring AOP切入點類型及系統提供的非常常用的切入點
Spring Cloud Gateway應用詳解1之謂詞