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