Webflux请求处理流程
spring mvc处理流程
在了解SpringMvc的请求流程源码之后,理解WebFlux就容易的多,毕竟WebFlux处理流程是模仿Servlet另起炉灶的。
下面是spring mvc的请求处理流程
具体步骤:
- 第一步:发起请求到前端控制器(DispatcherServlet) 
- 第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找) 
 匹配条件包括:请求路径、请求方法、header信息等
- 第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略 
 HandlerInterceptor是请求路径上的拦截器,需要自己实现这个接口以拦截请求,做一些对handler的前置和后置处理工作。
- 第四步:前端控制器调用处理器适配器去执行Handler 
- 第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler 
- 第六步:Handler执行完成给适配器返回ModelAndView 
- 第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view) 
- 第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可 
- 第九步:视图解析器向前端控制器返回View 
- 第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域) 
- 第十一步:前端控制器向用户响应结果 
webflux请求处理流程
图解:注解驱动请求的webflux请求处理流程

我们可以对比SpringMVC的请求流程图对比来看
我们可以看到,处理流程基本一样,有以下主要的点不同
- 处理核心
- WebFlux--DispatcherHandler
- SpringMvc--DispatcherServlet
 
- WebFlux--
- 返回值处理器
- WebFlux--HandlerResultHandler
- SpringMvc--HandlerMethodReturnValueHandler
 
- WebFlux--
- 内容协商配置器
- WebFlux--RequestedContentTypeResolverBuilder
- SpringMvc--ContentNegotiationConfigurer
 
- WebFlux--
还有很多就不一一例举了,想知道核心组件对比结果的同学,可以看下图。注意很多图上的组件名称相同,但是包的位置是不同的,所以大家要注意区分,不要弄混了。
Web MVC VS. WebFlux 核心组件对比
核心控制器DispatcherHandler
核心控制器DispatcherHandler,等同于阻塞方式的DispatcherServlet
DispatcherHandler实现ApplicationContextAware,那么必然会调用setApplicationContext方法
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        initStrategies(applicationContext);
    }
}
initStrategies初始化
获取HandlerMapping,HandlerAdapter,HandlerResultHandler的所有实例
protected void initStrategies(ApplicationContext context) {
    //获取HandlerMapping及其子类型的bean
    //HandlerMapping根据请求request获取handler执行链
    Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context, HandlerMapping.class, true, false);
    ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
    //排序
    AnnotationAwareOrderComparator.sort(mappings);
    this.handlerMappings = Collections.unmodifiableList(mappings);
    //获取HandlerAdapter及其子类型的bean
    Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context, HandlerAdapter.class, true, false);
    this.handlerAdapters = new ArrayList<>(adapterBeans.values());
    //排序
    AnnotationAwareOrderComparator.sort(this.handlerAdapters);
    //获取HandlerResultHandler及其子类型的bean
    Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context, HandlerResultHandler.class, true, false);
    this.resultHandlers = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(this.resultHandlers);
}
DispatcherHandler的总体流程:
- 1、通过 HandlerMapping(和DispathcherServlet中的HandlerMapping不同)获取到HandlerAdapter放到ServerWebExchange的属性中
- 2、获取到HandlerAdapter后触发handle方法,得到HandlerResult
- 3、通过HandlerResult,触发handleResult,针对不同的返回类找到不同的HandlerResultHandler如视图渲染ViewResolutionResultHandler、ServerResponseResultHandler、ResponseBodyResultHandler、ResponseEntityResultHandler不同容器有不同的实现,如Reactor,Jetty,Tomcat等。
HandlerMapping
webflux中引入了一个新的HandlerMapping,即RouterFunctionMapping
RouterFunctionMapping实现了InitializingBean,因此在其实例化的时候,会调用afterPropertiesSet方法
public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {
   @Nullable
   private RouterFunction<?> routerFunction;
    //省略部分代码
    //afterPropertiesSet()方法 是组件初始化后回调 必须实现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.
    * 从应用上下文中查找他们并初始化路由方法
    */
   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时实现依赖查找,查找所有的 RouterFunction beans
       //并注入到 List<RouterFunction<?>> 中。这样就会得到所有实现路由方法的集合
      @Autowired(required = false)
      public void setRouterFunctions(List<RouterFunction<?>> routerFunctions) {
         this.routerFunctions = routerFunctions;
      }
   }
}
HandlerAdapter
webflux中引入了一个新的HandlerAdapter,即HandlerFunctionAdapter
webflux中引入了一个新的HandlerResultHandler,即ServerResponseResultHandler
ServerResponseResultHandler实现了InitializingBean,因此在其实例化的时候,会调用afterPropertiesSet方法
流式处理请求handler()
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    //handlerMappings在initStrategies()方法中已经构造好了
    if (this.handlerMappings == null) {
        return createNotFoundError();
    }
    //构造Flux,数据源为handlerMappings集合
    return Flux.fromIterable(this.handlerMappings)
            //获取Mono<Handler>对象,通过concatMap保证顺序和handlerMappings顺序一致
            //严格保证顺序是因为在一个系统中可能存在一个Url有多个能够处理的HandlerMapping的情况
            .concatMap(mapping -> mapping.getHandler(exchange))
            .next()
            //如果next()娶不到值则抛出错误
            .switchIfEmpty(createNotFoundError())
            //触发HandlerApter的handle方法
            .flatMap(handler -> invokeHandler(exchange, handler))
            //触发HandlerResultHandler 的handleResult方法
            .flatMap(result -> handleResult(exchange, result));
}
触发HandlerApter的handle方法
private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
    return getResultHandler(result).handleResult(exchange, result)
            .onErrorResume(ex -> result.applyExceptionHandler(ex).flatMap(exceptionResult ->
                    getResultHandler(exceptionResult).handleResult(exchange, exceptionResult)));
}
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());
}
函数式端点请求处理流程
通过上图,我们可以看到,这个处理跟之前的注解驱动请求大有不同,但是请求的流程是万变不离其宗,只是组件有所变化。
接下来我们就跟着流程图一步一步的来解读WebFlux函数端点式请求的源码。
装配阶段
由上图我们可以看到 RouterFunctionMapping是由WebFluxConfigurationSupport创建的,接下来看一下RouterFunctions是怎么合并RouterFunction的并且如何关联到RouterFunctionMapping的。
RouterFunctionMapping 的源码,前面已经介绍了。
请求阶段
请求阶段的核心代码就是 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请求进来后执行的流程
   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.执行
         .flatMap(result -> handleResult(exchange, result));  //5. 处理结果
}
上面的代码已经把大部分的流程说明清楚了,那么我们来看一下lambda表达式中每个内部方法的具体实现。
首先我们来看一下步骤3的具体实现 org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler
@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
    //调用 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);  //通过路由获取到对应处理的HandlerFunction 也就是执行方法
   }
   else {
      return Mono.empty();
   }
}
获取到对应的HandlerFunction后我们就来执行第四步,调用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);  //执行handler 对应下面handle的方法
         }
      }
   }
   return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter#handle方法,这个类中的方法就是处理函数式端点请求的Adapter具体实现
@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模式 (返回值-参数)  无需准确的方法签名
         .map(response -> new HandlerResult(handlerFunction, response, HANDLER_FUNCTION_RETURN_TYPE));   //返回HandlerResult对象
}
这里的lambda模式比较难理解,主要是看HandlerFunction这个函数式接口
@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);
}
我们只需要满足 入参是ServerRequest类型 返回值是Mono<T> 就可以执行。
调用完具体方法之后,我们就可以进行返回值解析序列化了。这里就是步骤5 处理结果。
private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
    //获取对应的返回结果处理器并处理
   return getResultHandler(result).handleResult(exchange, result)
       //如果出现错误或者异常 则选择对应的异常结果处理器进行处理
         .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中都含有哪些返回值处理器
我们通过截图可以看出返回值解析器跟流程图一一对应。
在匹配到对应的返回值解析器之后进行返回值的封装和写会,这里要注意DataBuffer是NIO的写处理,最后写回到浏览器客户端。
Webflux请求处理流程的更多相关文章
- 04-SpringMVC之请求处理流程
		SpringMVC之请求处理流程 我们知道DispatcherServlet就是一个HttpServlet,而HttpServlet的请求就从doGet/doPost开始 DispatcherServ ... 
- ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程
		从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ... 
- ASP.Net请求处理机制初步探索之旅 - Part 5  ASP.Net MVC请求处理流程
		好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人>: --> 开篇:上一篇 ... 
- Http 请求处理流程
		引言 我查阅过不少Asp.Net的书籍,发现大多数作者都是站在一个比较高的层次上讲解Asp.Net.他们耐心.细致地告诉你如何一步步拖放控件.设置控件属性.编写CodeBehind代码,以实现某个特定 ... 
- Yii源码阅读笔记(二十一)——请求处理流程
		Yii2请求处理流程: 首先:项目路径/web/index.php (new yii\web\Application($config))->run();//根据配置文件创建App实例,先实例化y ... 
- 【转载】ASP.NET页面运行机制以及请求处理流程
		本文转至 ASP.NET页面运行机制以及请求处理流程 IIS处理页面的运行机制 IIS自身是不能处理像ASPX扩展名这样的页面,只能直接请求像HTML这样的静态文件,之所以能处理ASPX这样扩展名的页 ... 
- IIS架构与HTTP请求处理流程
		IIS架构与HTTP请求处理流程 Windows操作系统中的IIS负责提供互联网服务,一台运行了IIS的计算机可以看成是一台Web服务器. Windows XP SP2 中IIS主版本号为5,Wind ... 
- ASP.Net MVC请求处理流程
		ASP.Net MVC请求处理流程 好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人& ... 
- nginx系列9:HTTP反向代理请求处理流程
		HTTP反向代理请求处理流程 如下图: 
随机推荐
- 初始化mysql报错bin/mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory
			原因:缺少libaio.so.1 解决办法:安装即可 yum install -y libaio 
- idea 2018.3.3版本激活到
			新装的,还是试用版本,下面就是进行激活操作: 先下载 链接: https://pan.baidu.com/s/1o44bsO7tx3WGuO5GgT0ytw 提取码: gbmw 第一步:将bi ... 
- 狂神说Elasticsearch7.X学习笔记整理
			Elasticsearch概述 一.什么是Elasticsearch? Lucene简介 Lucene是一套用于全文检索和搜寻的开源程序库,由Apache软件基金会支持和提供 Lucene提供了一个简 ... 
- 还不懂 redis 持久化?看看这个
			Redis 是一个内存数据库,为了保证数据不丢失,必须把数据保存到磁盘,这就叫做持久化. Redis 有两种持久化方法: RDB 方式以及 AOF 方式 RDB 持久化 前言 RDB持久化把内存中的数 ... 
- Windows进程间通讯(IPC)----内存映射文件
			内存映射文件原理 内存映射文件是通过在虚拟地址空间中预留一块区域,然后通过从磁盘中已存在的文件为其调度物理存储器,访问此虚拟内存空间就相当于访问此磁盘文件了. 内存映射文件实现过程 HANDLE hF ... 
- React中diff算法的理解
			React中diff算法的理解 diff算法用来计算出Virtual DOM中改变的部分,然后针对该部分进行DOM操作,而不用重新渲染整个页面,渲染整个DOM结构的过程中开销是很大的,需要浏览器对DO ... 
- ES6学习-4 解构赋值(1)数组的解构赋值
			解构赋值是ES6很大的一个提升,为我们带来了很多方便,但用不好,会使程序的可读性变差,所以用时要注意,尽量保持程序的易读性. 数组解构赋值 在JS没有支持解构赋值之前,我们声明几个变量并赋值通常都是像 ... 
- 技能Get·BOM头是什么?
			阅文时长 | 0.26分钟 字数统计 | 472.8字符 主要内容 | 1.引言&背景 2.BOM头是什么? 3.如何创建或取消BOM头? 4.如何判断文件是否包含BOM头? 5.声明与参考资 ... 
- Linux 用户管理_用户相关配置文件详解
			linux的用户管理 linux支持多个用户同时使用同一个用户登陆系统,windows在修改组策略的情况下,也可以多个人使用同一个用户登陆. 远程连接Linux的方式:ssh 远程连接windows的 ... 
- Canal--服务端和客户端搭建
			服务端 源码编译 git clone https://github.com/alibaba/canal.git 用IDEA打开克隆的项目 等待maven下载完依赖后开始编译过程 依赖下载完成后,打开m ... 
