咱前面也看到了,也做过示例,咱知道 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
最大的不同点,是里面的实现绝大部分都采用响应式流编程。