DispatcherHandler的工作原理(函数式端点)

上一篇咱用传统的 WebMvc 编程风格测试了一个请求,下面咱用后面写的函数式端点编程开发的Handler,看看它的处理有什么相同和不同。

1. DispatcherHandler#handle

对,你没看错,它还是来到 DispatcherHandlerhandle 方法,证明两种方式最终都是走一个前端控制器。

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));
}

那套路就跟上一篇的一样了,咱一一来看:

2. 选定HandlerMapping

concatMap(mapping -> mapping.getHandler(exchange)) 的步骤是筛选 HandlerMapping 的,来到 AbstractHandlerMapping#getHandler

public Mono<Object> getHandler(ServerWebExchange exchange) {
    return getHandlerInternal(exchange).map(handler -> {
        // ......
    });
}

进入Lambda表达式中发现 Handler 的类型是我自己写的 RouterConfiguration 中的Lambda表达式!而且它也帮我映射到了实际处理的 DemoHandler

注意这个地方实际上的 HandlerMappingRouterFunctionMapping ,这个组件咱之前在第31篇留了个缺口,这里咱回顾一下:

2.0 RouterFunctionMapping

@Bean
public RouterFunctionMapping routerFunctionMapping() {
    RouterFunctionMapping mapping = createRouterFunctionMapping();
    mapping.setOrder(-1); // go before RequestMappingHandlerMapping
    mapping.setMessageReaders(serverCodecConfigurer().getReaders());
    mapping.setCorsConfigurations(getCorsConfigurations());

    return mapping;
}

它有设置一个 Order 的属性为 -1,并且后面的单行注释也写得很明白:它会排在 RequestMappingHandlerMapping 的前面。这样看来,用函数式端点开发的映射,优先级会高于用 WebMvc 的注解风格开发的映射

对比 RequestMappingHandlerMapping ,发现它的 Order 是0,证明 RouterFunctionMapping 更靠前:

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    mapping.setOrder(0);
    // ......

另外,RouterFunctionMapping 中包含了所有的函数式端点,咱看看它的定义:

public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {

    @Nullable
    private RouterFunction<?> routerFunction;

它里面有一个 routerFunction 的属性,它保存了所有的函数式端点。可这样看上去只有一个,说明它肯定有一些组合/合并的动作。

借助IDEA,发现在这个类中有一个 initRouterFunctions 方法:

protected void initRouterFunctions() {
    List<RouterFunction<?>> routerFunctions = routerFunctions();
    this.routerFunction = routerFunctions.stream().reduce(RouterFunction::andOther).orElse(null);
    logRouterFunctions(routerFunctions);
}

这里它会先获取所有的 RouterFunction ,之后在下面有一个将所有 RouterFunctionreduce 操作,它会利用 RouterFunctionandOther 方法,将所有 RouterFunction 进行组合。

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));
}

依旧来到 invokeHandler 方法中找 HandlerAdapter ,不过这次是函数式端点编程,所以找到的 HandlerAdapter 的类型会有所不同:

发现类型为 HandlerFunctionAdapter ,不过Debug时想展开看看它里面有什么组成,结果扑了个空。。。(实际上也确实没有对象属性)

4. 执行目标方法

public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
    HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
    ServerRequest request = exchange.getRequiredAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
    return handlerFunction.handle(request)
        .map(response -> new HandlerResult(handlerFunction, response, HANDLER_FUNCTION_RETURN_TYPE));
}

注意在 HandlerFunctionAdapter 中,所有的 Handler 都可以转换为 HandlerFunction 类型。

RouterFunctions 类的 route 方法会传入 HandlerFunction 类型的 lambda表达式:

public static <T extends ServerResponse> RouterFunction<T> route(
    RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
    return new DefaultRouterFunction<>(predicate, handlerFunction);
}

下面的return部分的调用,注意第一步:handlerFunction.handle(request) ,它会直接拿 handlerFunctionhandle 方法,这个操作就相当于实际调用 Controller 的方法,而 HandlerFunction 接口的定义:

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
    Mono<T> handle(ServerRequest request);
}

借助IDEA,发现 handle 方法的实现就是我自己写的 DemoHandler 里的方法:

由此可以发现,原来函数式端点的执行更简单,不需要走反射,直接强转就可以调用 Controller/Handler的目标方法。

5. 返回值处理

DispatcherHandler 的最后一步,要对上一步的返回值进行处理,会走 result -> handleResult(exchange, result) 方法:

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 ,不过这次获取的类型发生了变化:

它的类型是 ServerResponseHandlerResult !那咱就有必要看看这个类了,毕竟它跟 ResponseBodyResultHandler 肯定是不一样的实现。

5.1 ServerResponseHandlerResult

来到 ServerResponseHandlerResulthandleResult 方法:

public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
    ServerResponse response = (ServerResponse) result.getReturnValue();
    Assert.state(response != null, "No ServerResponse");
    return response.writeTo(exchange, new ServerResponse.Context() {
        @Override
        public List<HttpMessageWriter<?>> messageWriters() {
            return messageWriters;
        }
        @Override
        public List<ViewResolver> viewResolvers() {
            return viewResolvers;
        }
    });
}

可以发现它其实也没那么神秘,也是直接拿 ServerResponse ,直接调 writeTo 方法,向响应流中写入数据。

至此,请求被成功处理。

6. 流程图

7. 小结

  1. RouterFunctionMapping 用于在函数式端点的映射中寻找对应的目标方法,而且它的优先级高于 RequestMappingHandlerMapping

  2. 函数式端点编程在真正调用目标 Controller/Handler 方法时,相较于传统 WebMvc 方式,不需要走反射,而是直接强转为 HandlerFunction 后直接调用目标方法。

最后更新于