DispatcherHandler的工作原理(传统方式)
咱前面也看到了,也做过示例,咱知道 WebFlux 可以完美兼容 WebMvc 的开发风格,那自然咱就猜测,WebFlux 中的核心前端控制器 DispatcherHandler 估计能跟 DispatcherServlet 在这部分的处理上差不多,抱着这个猜测,咱来以Debug的方式来观察处理逻辑走向。
1. DispatcherHandler#handle
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
}
}这里很惊讶的发现它的源码非常少,相比较 DispatcherServlet 的代码数量,少的不是一个数量级!
注意它传入的参数是一个 ServerWebExchange ,联想 DispatcherServlet 中传入的是 HttpServletRequest 和 HttpServletResponse ,猜想它应该是类似于这两个类的合体:
public interface ServerWebExchange {
/**
* Return the current HTTP request.
*/
ServerHttpRequest getRequest();
/**
* Return the current HTTP response.
*/
ServerHttpResponse getResponse();果然从这个接口的方法中发现了 Request 和 Response 的概念,证明猜想正确。
回到 handle 方法,通过Debug进来,首先要判断现有的 HandlerMapping ,当前测试的Demo工程中我分别写了基于注解的映射,和基于函数式端点的映射。Debug下发现有3个 HandlerMapping :

之后的return结构中是一连串链式调用,这也体现出了函数式编程和响应式编程的一个弊端:Debug真的困难。我在最开始Debug找线索时,真是费了不少功夫,才慢慢找到这些端倪。
这段调用中可读性倒是挺高,咱参考之前 DispatcherServlet 的工作原理,大概分析一下这里面的三个步骤:
2. 选定HandlerMapping
不难看出,选定 HandlerMapping 的步骤是这几步:
首先进入 formInterable 方法:
2.1 Flux#fromIterable
它将这一组 HandlerMapping 转换成了一组 FluxIterable ,之后传入了 onAssembly 方法中。
2.2 onAssembly
方法的名称可以理解为 “触发了装载、扩展动作”,这个方法的文档注释:
To be used by custom operators: invokes assembly Hooks pointcut given a Flux, potentially returning a new Flux. This is for example useful to activate cross-cutting concerns at assembly time, eg. a generalized checkpoint().
由自定义运算符使用:给定Flux调用程序集Hooks切入点,并有可能返回新的Flux。这对于在装配时激活横切关注点很有用,比方说泛型checkpoint()。
文档注释中的几个关键词已经足以给我们提供思路了:切入点、装配、横切。是不是想到了AOP?其实这个方法的作用有点类似AOP,不过我个人感觉它应该更符合装饰者模式。通过Debug,发现它只是包装为一个 FluxIterable 对象,不过这就是装载、扩展的体现:将可迭代的集合装载为一个 Flux 流。

2.3 concatMap(mapping -> mapping.getHandler(exchange))
上面将那一组 HandlerMapping 转换为 Flux后,接下来要从这组 HandlerMapping 中找出能处理当前请求的 HandlerMapping 了:
来到 AbstractHandlerMapping#getHandler :
这个方法分为两部分:先执行 getHandlerInternal 获取可以处理当前请求的 HandlerMapping ,再执行 map 方法进行后处理。
2.3.1 getHanderInternal
对于传统的 @RequestMapping 方式标注的 “Handler”,会进入 AbstractHandlerMethodMapping#getHandlerInternal 方法(又跟 WebMvc 非常相似):
对比第26篇 (DispatcherServlet 的工作原理),第5.2.1章节,发现 DispatcherServlet 中的方法更短,但相比较而言 WebFlux 中处理的更严谨(有异常处理的考虑),而WebMvc 中只是简单地把异常抛出去了而已。如果不看这些细枝末节,会发现思路完全一致:先搜索所有 “HandlerMethod”,后封装为一个单独的 HandlerMethod 类型的Bean。
方法内部的实现与 WebMvc 部分思路几乎完全一致,不再深扒,这里咱瞄一眼扫描到的匹配请求的 HandlerMethod :

它已经找到了我写的 DemoController 。
2.3.2 搜索到后的map
可以看出来它这里面有对跨域的处理,由于咱这是最简单的测试,自然不会触发跨域处理,直接把封装好的 Mono<HandlerMethod> 返回回去了。
返回回去后,回到 DispatcherHandler 的 handle 方法,要寻找这个 HandlerMethod 对应的 HandlerAdapter 了:
3. 寻找HandlerAdapter
由于是 WebMvc 开发风格的处理,返回的 HandlerMethod 也是 DemoController 里的,默认情况下有3个 HandlerAdapter :

不用想,肯定走 RequestMappingHandlerAdapter ,通过Debug发现确实如此:

之后直接调了它的 handle 方法了!
4. 【目标方法】HandlerAdapter#handle
来到 RequestMappingHandlerAdapter 中:
这里面先是对 HandlerMethod 进行一些处理,以及异常处理器的处理,最后的链式调用中第三行有一个 invocableMethod.invoke ,它就会去引导调用真正的 Controller 方法。根据上面源码的注释标注咱一样一样来看:
4.1 InitBinderBindingContext
这个类非常有意思,它在 SpringFramework 的官方API中根本没有收录进去,而且也找不到任何相关的资料,只能靠源码的部分来大概看一眼它:
它的文档注释中提到了 @InitBinder 注解,这个注解咱之前在 WebMvc 部分见过,它是做参数绑定的。另外它继承自 BindingContext ,而 BindingContext 咱看类名能猜测可能是跟 @InitBinder 注解配合做参数绑定相关工作的。看一眼它的文档注释:
Context to assist with binding request data onto Objects and provide access to a shared Model with controller-specific attributes. Provides methods to create a WebExchangeDataBinder for a specific target, command Object to apply data binding and validation to, or without a target Object for simple type conversion from request values. Container for the default model for the request.
用于帮助将请求数据绑定到对象的上下文,并提供对具有控制器特定属性的共享模型的访问。
提供用于为特定目标创建 WebExchangeDataBinder 的方法,提供对目标对象应用数据绑定和验证的命令Object(或不具有目标对象的命令Object)以从请求值进行简单类型转换的方法。
请求的默认模型的容器。
文档注释倒是可以比较明确的看出,它确实是跟参数绑定相关的组件。
4.2 InvocableHandlerMethod
这个组件咱之前在 WebMvc 部分也见过,它的作用就是调用 HandlerMapping 中真正封装的 Controller 方法。注意到它跟 WebMvc 部分的 ServletInvocableHandlerMethod 应该是继承和被继承关系,只不过在 WebFlux 中见到的 InvocableHandlerMethod 是在 reactive 包下的,而不是 mvc 包。其余部分几乎完全相同,小伙伴们可以将之前第26篇的 DispatcherServlet 工作原理进行对照翻看,小册不再赘述。
4.3 执行目标方法,处理返回值和异常
注意这里面最重要的动作就是 invocableMethod.invoke(exchange, bindingContext) ,来看它的实现,进入到 InvocableHandlerMethod 中:
它直接就 return 了,里面又是流式调用,lambda表达式中有一部分吸引了我:try块中的 value = getBridgedMethod().invoke(getBean(), args); ,这个套路似曾相识啊,咱把 WebMvc 中的调用方式一块拿出来对比一下:
4.3.1 WebMvc 和 WebFlux 部分的Controller方法反射调用
竟然是一模一样的!是不是产生了一种感觉:WebFlux 不会基本上都是抄的 WebMvc 吧。。。(然而事实还真是)
执行完目标方法后,回到 invoke 方法,要进行返回值处理:
上面处理响应状态后,下面要处理返回值了:
4.3.2 处理返回值
由于之前咱在 DemoController 中声明的返回值类型是 Mono<String> ,那这里的 value 也就是 Mono<String> 类型。通过Debug,发现它的类型和对应的 ReactiveAdapter 也都获取到了:

多提一嘴,ReactiveAdapter 就是通过上一篇咱看自动装配中看到的 ReactiveAdapterRegistry 组件获取而来,用它来实际兼容 RxJava 等其他响应式编程框架。
最后,它会将返回值封装为一个 HandlerResult 对象,返回出去。
5. 最后的返回值处理
回到 DispatcherHandler 中:
上一步执行完成的是 handler -> invokeHandler ,下面还要再处理上一步的返回值:
又是一个调用链,它先根据上一步的返回值,获取到 ResultHandler ,之后处理一下,返回。
5.1 getResultHandler
它的搜索方法还是循环所有的 HandlerResultHandler ,而默认情况下一共有4个 HandlerResultHandler :

很容易判断,通过 @RestController 出来的方法,都会走 ResponseBodyResultHandler :

5.2 ResponseBodyResultHandler#handleResult
在这里它会将返回值拿到,之后执行 writeBody 方法将返回值结果写到响应流中,处理结束。
handleResult 方法执行完毕后,回到 DispatcherHandler 中,链式调用的链也执行完毕了,整个请求处理结束。
6. 流程图

7. 小结
DispatcherHandler与DispatcherServlet的处理思路几乎完全相同,包括寻找HandlerMapping、HandlerAdapter、执行目标方法等。DispatcherHandler相比较DispatcherServlet最大的不同点,是里面的实现绝大部分都采用响应式流编程。
最后更新于
这有帮助吗?