Springboot 2.0---WebFlux請求處理流程

筆記是學(xué)習(xí)了小馬哥在慕課網(wǎng)的課程的《Spring Boot 2.0深度實踐之核心技術(shù)篇》的內(nèi)容結(jié)合自己的需要和理解做的筆記。

前言

在了解了WebFlux核心組件之后,我們就該了解相應(yīng)的請求流程了,在之前就寫過幾篇關(guān)于 SpringMvc 請求的流程筆記,如果之前有過了解的并且自己debugger 過的小伙伴,相信了解 WebFlux 的執(zhí)行流程會很快。

我們知道WebFlux支持兩種請求模式

  • 注解驅(qū)動
  • 函數(shù)式端點

接下來我們會重點講一下函數(shù)式端點請求的具體流程,注解驅(qū)動由于跟SpringMVC很像,所以大家只要看一下流程圖,基本就可以了解了。

注解驅(qū)動組件請求處理流程

流程圖

下面我們先來看一下具體流程圖

p14.png

我們可以對比SpringMVC的請求流程圖對比來看

p16.png

我們可以看到,處理流程基本一樣,有以下主要的點不同

  • 處理核心

    • WebFlux--DispatcherHandler
    • SpringMvc--DispatcherServlet
  • 返回值處理器

    • WebFlux--HandlerResultHandler
    • SpringMvc--HandlerMethodReturnValueHandler
  • 內(nèi)容協(xié)商配置器

    • WebFlux--RequestedContentTypeResolverBuilder
    • SpringMvc--ContentNegotiationConfigurer

還有很多就不一一例舉了,想知道核心組件對比結(jié)果的同學(xué),可以看下圖。注意很多圖上的組件名稱相同,但是包的位置是不同的,所以大家要注意區(qū)分,不要弄混了。

Web MVC VS. WebFlux 核心組件對比

p17.png

函數(shù)式端點請求處理流程

流程圖

按照慣例,上流程圖

p15.png

通過上圖,我們可以看到,這個處理跟之前的注解驅(qū)動請求大有不同,但是請求的流程是萬變不離其宗,只是組件有所變化。

源碼解讀

接下來我們就跟著流程圖一步一步的來解讀WebFlux函數(shù)端點式請求的源碼。

裝配階段

由上圖我們可以看到 RouterFunctionMapping是由WebFluxConfigurationSupport創(chuàng)建的,在之前的筆記中我們已經(jīng)有說明。

我們接下來看一下RouterFunctions是怎么合并RouterFunction的并且如何關(guān)聯(lián)到RouterFunctionMapping的。

我們先來看一下 RouterFunctionMapping關(guān)于裝配階段的代碼。

public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {

   @Nullable
   private RouterFunction<?> routerFunction;
    //省略部分代碼
    
    //afterPropertiesSet()方法 是組件初始化后回調(diào) 必須實現(xiàn)InitializingBean接口
    //
   @Override
   public void afterPropertiesSet() throws Exception {
      if (CollectionUtils.isEmpty(this.messageReaders)) {
         ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create();
         this.messageReaders = codecConfigurer.getReaders();
      }

      //初始化routerFunction
      if (this.routerFunction == null) {
         initRouterFunctions();
      }
   }

   /**
    * Initialized the router functions by detecting them in the application context.
    * 從應(yīng)用上下文中查找他們并初始化路由方法
    */
   protected void initRouterFunctions() {
      if (logger.isDebugEnabled()) {
         logger.debug("Looking for router functions in application context: " +
               getApplicationContext());
      }

      //查找合并所有路由方法的bean
      List<RouterFunction<?>> routerFunctions = routerFunctions();
      if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
         routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
      }
       
      //將一個請求中含有多個路由請求方法合并成一個方法
      this.routerFunction = routerFunctions.stream()
            .reduce(RouterFunction::andOther)
            .orElse(null);
   }

    //查找并合并所有路由方法
   private List<RouterFunction<?>> routerFunctions() {
       //聲明 SortedRouterFunctionsContainer bean
      SortedRouterFunctionsContainer container = new SortedRouterFunctionsContainer();
       //自動注入到上下文中 
      obtainApplicationContext().getAutowireCapableBeanFactory().autowireBean(container);
      //返回路由
      return CollectionUtils.isEmpty(container.routerFunctions) ? Collections.emptyList() :
            container.routerFunctions;
   }
    //省略部分代碼
   private static class SortedRouterFunctionsContainer {

      @Nullable
      private List<RouterFunction<?>> routerFunctions;

       //由上面的方法 自動注入bean時實現(xiàn)依賴查找,查找所有的 RouterFunction beans
       //并注入到 List<RouterFunction<?>> 中。這樣就會得到所有實現(xiàn)路由方法的集合
      @Autowired(required = false)
      public void setRouterFunctions(List<RouterFunction<?>> routerFunctions) {
         this.routerFunctions = routerFunctions;
      }
   }

}

請求階段

請求階段的核心代碼就是 org.springframework.web.reactive.DispatcherHandler#handle方法,我們來看一下源碼。

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
   if (logger.isDebugEnabled()) {
      ServerHttpRequest request = exchange.getRequest();
      logger.debug("Processing " + request.getMethodValue() + " request for [" + request.getURI() + "]");
   }
   if (this.handlerMappings == null) {
      return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);
   }
   // 1.HTTP請求進來后執(zhí)行的流程
   return Flux.fromIterable(this.handlerMappings)  //2 遍歷handlerMappings定位RouterFunctionMapping
         .concatMap(mapping -> mapping.getHandler(exchange))   // 3.獲取HandlerFunction
         .next()
         .switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION))  
         .flatMap(handler -> invokeHandler(exchange, handler))   //4.執(zhí)行
         .flatMap(result -> handleResult(exchange, result));  //5. 處理結(jié)果
}

上面的代碼已經(jīng)把大部分的流程說明清楚了,那么我們來看一下lambda表達式中每個內(nèi)部方法的具體實現(xiàn)。

首先我們來看一下步驟3的具體實現(xiàn) org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler

@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
    //調(diào)用 getHandlerInternal 方法來確定HandlerFunction
   return getHandlerInternal(exchange).map(handler -> {
      if (CorsUtils.isCorsRequest(exchange.getRequest())) {
         CorsConfiguration configA = this.globalCorsConfigSource.getCorsConfiguration(exchange);
         CorsConfiguration configB = getCorsConfiguration(handler, exchange);
         CorsConfiguration config = (configA != null ? configA.combine(configB) : configB);
         if (!getCorsProcessor().process(config, exchange) ||
               CorsUtils.isPreFlightRequest(exchange.getRequest())) {
            return REQUEST_HANDLED_HANDLER;
         }
      }
      return handler;
   });
}

上面一大段代碼其實主要來獲取handler的方法是 getHandlerInternal(exchange) 剩下的部分是 跨域處理的邏輯。我們看一下 這個方法。

@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
   if (this.routerFunction != null) {
      ServerRequest request = ServerRequest.create(exchange, this.messageReaders);
      exchange.getAttributes().put(RouterFunctions.REQUEST_ATTRIBUTE, request);
      return this.routerFunction.route(request);  //通過路由獲取到對應(yīng)處理的HandlerFunction 也就是執(zhí)行方法
   }
   else {
      return Mono.empty();
   }
}

獲取到對應(yīng)的HandlerFunction后我們就來執(zhí)行第四步,調(diào)用HandlerFunction。

private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
         if (handlerAdapter.supports(handler)) {  //判斷HandlerAdapters中是否支持之前獲取到的handler
            return handlerAdapter.handle(exchange, handler);  //執(zhí)行handler 對應(yīng)下面handle的方法
         }
      }
   }
   return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}

org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter#handle方法,這個類中的方法就是處理函數(shù)式端點請求的Adapter具體實現(xiàn)

@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
   HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
   ServerRequest request = exchange.getRequiredAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
   return handlerFunction.handle(request)   //由lambda模式 (返回值-參數(shù))  無需準(zhǔn)確的方法簽名
         .map(response -> new HandlerResult(handlerFunction, response, HANDLER_FUNCTION_RETURN_TYPE));   //返回HandlerResult對象 
}

這里的lambda模式比較難理解,我也是看了好幾遍才理解馬哥所說,主要是看HandlerFunction這個函數(shù)式接口

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {

   /**
    * Handle the given request.
    * @param request the request to handle
    * @return the response
    */
   Mono<T> handle(ServerRequest request);

}

我們只需要滿足 入?yún)⑹?code>ServerRequest類型 返回值是Mono<T> 就可以執(zhí)行。

調(diào)用完具體方法之后,我們就可以進行返回值解析序列化了。這里就是步驟5 處理結(jié)果。

private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
    //獲取對應(yīng)的返回結(jié)果處理器并處理          
   return getResultHandler(result).handleResult(exchange, result)   
       //如果出現(xiàn)錯誤或者異常 則選擇對應(yīng)的異常結(jié)果處理器進行處理
         .onErrorResume(ex -> result.applyExceptionHandler(ex).flatMap(exceptionResult ->                   getResultHandler(exceptionResult).handleResult(exchange, exceptionResult)));
}

我們再來看一下getResultHandler代碼

private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
   if (this.resultHandlers != null) {
      for (HandlerResultHandler resultHandler : this.resultHandlers) {
         if (resultHandler.supports(handlerResult)) {
            return resultHandler;
         }
      }
   }
   throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
}

在這里我們看一下resultHandlers中都含有哪些返回值處理器

p18.png

我們通過截圖可以看出返回值解析器跟流程圖一一對應(yīng)。

之后的8,9步就是在匹配到對應(yīng)的返回值解析器之后進行返回值的封裝和寫會,這里要注意DataBuffer是NIO的寫處理,最后寫回到瀏覽器客戶端。

總結(jié)

其實在了解SpringMvc的請求流程源碼之后,理解WebFlux就容易的多,畢竟WebFlux處理流程是模仿Servlet另起爐灶的。如果感興趣的小伙伴可以跟著我的思路自己寫一個請求方法去debugger一下加深理解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容