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
的步骤是这几步:
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
首先进入 formInterable
方法:
2.1 Flux#fromIterable
public static <T> Flux<T> fromIterable(Iterable<? extends T> it) {
return onAssembly(new FluxIterable<>(it));
}
它将这一组 HandlerMapping
转换成了一组 FluxIterable
,之后传入了 onAssembly
方法中。
2.2 onAssembly
protected static <T> Flux<T> onAssembly(Flux<T> source) {
Function<Publisher, Publisher> hook = Hooks.onEachOperatorHook;
if(hook != null) {
source = (Flux<T>) hook.apply(source);
}
if (Hooks.GLOBAL_TRACE) {
AssemblySnapshot stacktrace = new AssemblySnapshot(null, Traces.callSiteSupplierFactory.get());
source = (Flux<T>) Hooks.addAssemblyInfo(source, stacktrace);
}
return source;
}
方法的名称可以理解为 “触发了装载、扩展动作”,这个方法的文档注释:
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
:
public Mono<Object> getHandler(ServerWebExchange exchange) {
return getHandlerInternal(exchange).map(handler -> {
// ......
});
}
这个方法分为两部分:先执行 getHandlerInternal
获取可以处理当前请求的 HandlerMapping
,再执行 map
方法进行后处理。
2.3.1 getHanderInternal
对于传统的 @RequestMapping
方式标注的 “Handler”,会进入 AbstractHandlerMethodMapping#getHandlerInternal
方法(又跟 WebMvc 非常相似):
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod;
try {
// 搜索处理器方法(真正处理请求的RequestMapping)
handlerMethod = lookupHandlerMethod(exchange);
}
catch (Exception ex) {
return Mono.error(ex);
}
// 将方法分离出来,单独形成一个Bean
if (handlerMethod != null) {
handlerMethod = handlerMethod.createWithResolvedBean();
}
return Mono.justOrEmpty(handlerMethod);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
对比第26篇 (DispatcherServlet 的工作原理),第5.2.1章节,发现 DispatcherServlet
中的方法更短,但相比较而言 WebFlux 中处理的更严谨(有异常处理的考虑),而WebMvc 中只是简单地把异常抛出去了而已。如果不看这些细枝末节,会发现思路完全一致:先搜索所有 “HandlerMethod”,后封装为一个单独的 HandlerMethod
类型的Bean。
方法内部的实现与 WebMvc 部分思路几乎完全一致,不再深扒,这里咱瞄一眼扫描到的匹配请求的 HandlerMethod
:

它已经找到了我写的 DemoController
。
2.3.2 搜索到后的map
return getHandlerInternal(exchange).map(handler -> {
if (logger.isDebugEnabled()) {
logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
}
if (CorsUtils.isCorsRequest(exchange.getRequest())) {
CorsConfiguration configA = this.corsConfigurationSource.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;
});
可以看出来它这里面有对跨域的处理,由于咱这是最简单的测试,自然不会触发跨域处理,直接把封装好的 Mono<HandlerMethod>
返回回去了。
返回回去后,回到 DispatcherHandler
的 handle
方法,要寻找这个 HandlerMethod
对应的 HandlerAdapter
了:
3. 寻找HandlerAdapter
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
if (this.handlerAdapters != null) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter.handle(exchange, handler);
}
}
}
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
由于是 WebMvc 开发风格的处理,返回的 HandlerMethod
也是 DemoController
里的,默认情况下有3个 HandlerAdapter :

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

之后直接调了它的 handle
方法了!
4. 【目标方法】HandlerAdapter#handle
来到 RequestMappingHandlerAdapter
中:
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Assert.state(this.methodResolver != null && this.modelInitializer != null, "Not initialized");
// 4.1 初始化参数绑定上下文
InitBinderBindingContext bindingContext = new InitBinderBindingContext(
getWebBindingInitializer(), this.methodResolver.getInitBinderMethods(handlerMethod));
// 4.2 创建方法执行对象
InvocableHandlerMethod invocableMethod = this.methodResolver.getRequestMappingMethod(handlerMethod);
// 异常处理器的准备
Function<Throwable, Mono<HandlerResult>> exceptionHandler =
ex -> handleException(ex, handlerMethod, bindingContext, exchange);
// 4.3 执行目标方法,处理返回值和异常
return this.modelInitializer
.initModel(handlerMethod, bindingContext, exchange)
.then(Mono.defer(() -> invocableMethod.invoke(exchange, bindingContext)))
.doOnNext(result -> result.setExceptionHandler(exceptionHandler))
.doOnNext(result -> bindingContext.saveModel())
.onErrorResume(exceptionHandler);
}
这里面先是对 HandlerMethod
进行一些处理,以及异常处理器的处理,最后的链式调用中第三行有一个 invocableMethod.invoke
,它就会去引导调用真正的 Controller 方法。根据上面源码的注释标注咱一样一样来看:
4.1 InitBinderBindingContext
这个类非常有意思,它在 SpringFramework 的官方API中根本没有收录进去,而且也找不到任何相关的资料,只能靠源码的部分来大概看一眼它:
/** Extends BindingContext with @InitBinder method initialization. */
class InitBinderBindingContext extends BindingContext
它的文档注释中提到了 @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 执行目标方法,处理返回值和异常
return this.modelInitializer
.initModel(handlerMethod, bindingContext, exchange)
.then(Mono.defer(() -> invocableMethod.invoke(exchange, bindingContext)))
.doOnNext(result -> result.setExceptionHandler(exceptionHandler))
.doOnNext(result -> bindingContext.saveModel())
.onErrorResume(exceptionHandler);
注意这里面最重要的动作就是 invocableMethod.invoke(exchange, bindingContext)
,来看它的实现,进入到 InvocableHandlerMethod
中:
public Mono<HandlerResult> invoke(
ServerWebExchange exchange, BindingContext bindingContext, Object... providedArgs) {
return getMethodArgumentValues(exchange, bindingContext, providedArgs).flatMap(args -> {
Object value;
try {
//4.3.1 执行目标方法
ReflectionUtils.makeAccessible(getBridgedMethod());
value = getBridgedMethod().invoke(getBean(), args);
}
// catch ......
// ......
});
}
它直接就 return 了,里面又是流式调用,lambda表达式中有一部分吸引了我:try块中的 value = getBridgedMethod().invoke(getBean(), args);
,这个套路似曾相识啊,咱把 WebMvc 中的调用方式一块拿出来对比一下:
4.3.1 WebMvc 和 WebFlux 部分的Controller方法反射调用
// org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod (注意看包名)
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
------ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ------
// org.springframework.web.method.support.InvocableHandlerMethod (注意看包名)
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
return getBridgedMethod().invoke(getBean(), args);
}
// catch ......
}
竟然是一模一样的!是不是产生了一种感觉:WebFlux 不会基本上都是抄的 WebMvc 吧。。。(然而事实还真是)
执行完目标方法后,回到 invoke
方法,要进行返回值处理:
try {
ReflectionUtils.makeAccessible(getBridgedMethod());
value = getBridgedMethod().invoke(getBean(), args);
} // catch ......
// 处理响应状态
HttpStatus status = getResponseStatus();
if (status != null) {
exchange.getResponse().setStatusCode(status);
}
MethodParameter returnType = getReturnType();
// 4.3.2 处理返回值
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(returnType.getParameterType());
boolean asyncVoid = isAsyncVoidReturnType(returnType, adapter);
if ((value == null || asyncVoid) && isResponseHandled(args, exchange)) {
return (asyncVoid ? Mono.from(adapter.toPublisher(value)) : Mono.empty());
}
HandlerResult result = new HandlerResult(this, value, returnType, bindingContext);
return Mono.just(result);
上面处理响应状态后,下面要处理返回值了:
4.3.2 处理返回值
由于之前咱在 DemoController
中声明的返回值类型是 Mono<String>
,那这里的 value 也就是 Mono<String>
类型。通过Debug,发现它的类型和对应的 ReactiveAdapter
也都获取到了:

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

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

5.2 ResponseBodyResultHandler#handleResult
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
Object body = result.getReturnValue();
MethodParameter bodyTypeParameter = result.getReturnTypeSource();
return writeBody(body, bodyTypeParameter, exchange);
}
在这里它会将返回值拿到,之后执行 writeBody
方法将返回值结果写到响应流中,处理结束。
handleResult
方法执行完毕后,回到 DispatcherHandler
中,链式调用的链也执行完毕了,整个请求处理结束。
6. 流程图

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