SpringBoot源码解读与原理分析(三十九)SpringBoot整合WebWebFlux(二)DispatcherHandler的传统方式和函数式端点工作原理

前言

DispatcherHandler作为WebFlux的核心前端控制器,它的作用与WebMvc中的DispatcherServlet相同,都是负责统一接收客户端请求并处理,然后将结果响应给客户端。

由于WebFlux可以完美兼容@RequestMapping注解式开发和函数式端点开发,因此DispatcherHandler的工作原理也可以分为传统方式和函数式。

13.4 DispatcherHandler的传统方式工作原理

SpringBoot源码解读与原理分析(三十八)SpringBoot整合WebFlux(一)WebFlux的自动装配 13.2 SpringBoot整合WebFlux示例项目 中,已构建示例项目,启动该项目,并将断点打在DispatcherHandler的handle方法中,在浏览器访问 http://127.0.0.1:8080/hello开始调试。

源码1DispatcherHandler.java

@Override
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));
}
源码2ServerWebExchange.java

public interface ServerWebExchange {
    ServerHttpRequest getRequest();
    ServerHttpResponse getResponse();
    // ......
}

由 源码1-2 可知,handle方法的入参是一个ServerWebExchange对象,而ServerWebExchange对象是request和response的组合体。

handle方法的if分支中,会检查DispatcherHandler中是否已注册有HandlerMapping,由于WebFlux支持@RequestMapping注解式开发,在其自动配置类中就已经注册了必要的HandlerMapping,因此程序不会进入该if分支。后面的return结构是一串链式调用。

13.4.1 筛选HandlerMapping

链式调用的第一步,是寻找可以匹配当前请求的HandlerMapping对象,其代码如下:

return Flux.fromIterable(this.handlerMappings)
            .concatMap(mapping -> mapping.getHandler(exchange))
            .next()
            // ......

首先会将DispatcherHandler中保存在的所有HandlerMapping封装为一个Flux对象,之后使用concatMap方法将所有的HandlerMapping都尝试匹配当前请求,并将结果收集合并,最后调用next方法提取出第一个匹配成功的HandlerMapping对象。

13.4.1.1 concatMap
源码3Flux.java

public final <V> Flux<V> concatMap(Function<? super T, ? extends Publisher<? extends V>> mapper) {
    return concatMap(mapper, Queues.XS_BUFFER_SIZE);
}

由 源码3 可知,concatMap方法是Flux中的一个动作,可以将Flux管道中的对象转换为另一种类型的对象,且管道中的元素个数可能发生改变。

因此,DispatcherHandler中调用concatMap方法的目的在于,通过一个循环后将仅支持当前请求的HandlerMapping对象筛选出来,忽略不支持当前请求的HandlerMapping对象。

13.4.1.2 mapping.getHandler

筛选HandlerMapping对象的实际动作需要借助HandlerMapping的getHandler方法,如果该方法返回的Mono对象中有具体值则认为匹配成功,反之匹配失败。

getHandler方法定义在所有HandlerMapping实现类的基础父类AbstractHandlerMapping中。

源码4AbstractHandlerMapping.java

@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
    return getHandlerInternal(exchange).map(handler -> {
        // logger ....
        // 跨域相关处理 ...
        return handler;
    });
}

由 源码4 可知,getHandler方法内部又会调用模板方法getHandlerInternal

基于@RequestMapping注解式开发的解析,底层由RequestMappingHandlerMapping负责匹配,而getHandlerInternal方法的实现逻辑在父类AbstractHandlerMethodMapping中。

源码5AbstractHandlerMethodMapping.java

@Override
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
    this.mappingRegistry.acquireReadLock();
    try {
        HandlerMethod handlerMethod;
        try {
            // 寻找可以匹配当前请求的HandlerMethod
            handlerMethod = lookupHandlerMethod(exchange);
        } // catch ...
        // 创建一个全新的HandlerMethod对象并返回
        if (handlerMethod != null) {
            handlerMethod = handlerMethod.createWithResolvedBean();
        }
        return Mono.justOrEmpty(handlerMethod);
    } finally {
        this.mappingRegistry.releaseReadLock();
    }
}

由 源码5 可知,getHandlerInternal方法首先会调用lookupHandlerMethod方法寻找可以匹配当前请求的HandlerMethod。

通过Debug,可以发现此处成功匹配到/hello请求对应的Controller方法:

成功匹配到Controller方法
随后,会再调用createWithResolvedBean方法创建一个全新的HandlerMethod对象并返回。为了什么要再次创建呢?

观察上图可以发现,调用createWithResolvedBean方法之前HandlerMethod对象中的bean属性是一个字符串,而不是真正的bean对象。因此,调用createWithResolvedBean方法的目的就在于创建一个真正的bean对象。

通过Debug,可以发现return之前,真正的bean对象已被创建:

真正的bean对象已被创建

13.4.2 筛选HandlerAdapter并执行

HandlerMapping筛选出来后,DispatcherHandler的下一个步骤是筛选合适的HandlerAdapter,用于执行目标Handler。其源码是.flatMap(handler -> invokeHandler(exchange, handler))部分。

13.4.2.1 筛选HandlerAdapter
源码6DispatcherHandler.java

private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
    if (this.handlerAdapters != null) {
        // 遍历全部HandlerAdapter
        for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
            if (handlerAdapter.supports(handler)) {
                // 筛选出支持当前请求的,直接执行
                return handlerAdapter.handle(exchange, handler);
            }
        }
    }
    return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}

由 源码6 可知,invokeHandler方法会逐个检查DispatcherHandler中的HandlerAdapter是否支持执行当前的Handler,如果支持则直接调用其handle方法执行目标Handler。

源码7RequestMappingHandlerAdapter.java

@Override
public boolean supports(Object handler) {
    return handler instanceof HandlerMethod;
}

由 源码7 可知,判断HandlerAdapter是否支持执行当前的Handler的方法仅仅是判断Handler对象的类型是否是HandlerMethod。

13.4.2.2 执行Handler

基于@RequestMapping注解式开发的Handler,底层调用RequestMappingHandlerAdapter的handle方法执行。

源码8RequestMappingHandlerAdapter.java

@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    // ......
    // 封装InvocableHandlerMethod
    InvocableHandlerMethod invocableMethod = this.methodResolver.getRequestMappingMethod(handlerMethod);
    // ......
    // 链式调用invocableMethod.invoke执行
    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);
}

由 源码8 可知,handle方法中首先会将HandlerMethod封装为InvocableHandlerMethod,然后在链式调用中执行invocableMethod.invoke方法,利用反射机制执行目标Handler。

13.4.3 返回值处理

InvocableHandlerMethod的invoke方法执行完成后返回一个HandlerResult对象,其中封装了执行Controller方法后返回的真实值。

DispatcherHandler的最后一个关键步骤是调用handleResult方法对该返回值进行处理。其源码是.flatMap(result -> handleResult(exchange, result))部分。

源码9DispatcherHandler.java

private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
    return getResultHandler(result).handleResult(exchange, result)
            .checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]")
            .onErrorResume(ex ->
                    result.applyExceptionHandler(ex).flatMap(exResult -> {
                        String text = "Exception handler " + exResult.getHandler() +
                                ", error=\"" + ex.getMessage() + "\" [DispatcherHandler]";
                        return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text);
                    }));
}

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

由 源码8-9 可知,handleResult方法首先会调用getResultHandler方法筛选出可以处理当前请求返回的HandlerResultHandler对象,再调用其handleResult方法真正进行返回值处理。

通过Debug,可以发现getResultHandler方法中可选的HandlerResultHandler有4个,恰好就是WebFluxConfigurationSupport配置类中通过@Bean注解注册的4种ResultHandler。

可选的HandlerResultHandler有4个
基于@RequestMapping注解式开发的Handler,其最终使用的HandlerResultHandler一定是ResponseBodyResultHandler。

源码10ResponseBodyResultHandler.java

@Override
public boolean supports(HandlerResult result) {
    MethodParameter returnType = result.getReturnTypeSource();
    Class<?> containingClass = returnType.getContainingClass();
    return (AnnotatedElementUtils.hasAnnotation(containingClass, ResponseBody.class) ||
            returnType.hasMethodAnnotation(ResponseBody.class));
}

@Override
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
    Object body = result.getReturnValue();
    MethodParameter bodyTypeParameter = result.getReturnTypeSource();
    return writeBody(body, bodyTypeParameter, exchange);
}

由 源码10 可知,判断ResponseBodyResultHandler是否支持的依据就是Handler是否标注@ResponseBody注解;而真正处理返回值的handleResult方法会将Handler的返回值取出,并执行writeBody方法将返回值结果写入响应流。

ResultHandler的工作处理完后,handle方法的链式调用执行完毕,一次完整的请求处理完毕。

13.4.4 工作流程小结

基于@RequestMapping注解时开发的工作流程如下:

基于@RequestMapping注解时开发的工作流程

13.5 DispatcherHandler的函数式端点工作原理

使用传统的@RequestMapping注解式开发与使用函数式端点开发的最大区别在于,底层支持的HandlerMapping和HandlerAdapter的实现类不同

对于整体的请求而言,其起点仍然是DispatcherHandler的handle方法。

使用浏览器访问http://127.0.0.1:8080/hello3发起请求,开始Debug调试。

13.5.1 HandlerMapping的不同

对于函数式端点开发的Handler,底层使用的HandlerMapping是RouterFunctionMapping。

源码11RouterFunctionMapping.java

public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        // ......
        if (this.routerFunction == null) {
            initRouterFunctions();
        }
    }
}

protected void initRouterFunctions() {
    // 提取出IOC容器中注册的所有RouterFunction对象,并收集为一个List
    List<RouterFunction<?>> routerFunctions = routerFunctions();
    // 将所有RouterFunction组合为一个RouterFunction对象
    this.routerFunction = routerFunctions.stream().reduce(RouterFunction::andOther).orElse(null);
    logRouterFunctions(routerFunctions);
}

private List<RouterFunction<?>> routerFunctions() {
    List<RouterFunction<?>> functions = obtainApplicationContext()
            .getBeanProvider(RouterFunction.class)
            .orderedStream()
            .map(router -> (RouterFunction<?>) router)
            .collect(Collectors.toList());
    return (!CollectionUtils.isEmpty(functions) ? functions : Collections.emptyList());
}

由 源码11 可知,RouterFunctionMapping本身实现了InitializingBean,因此在它初始化时会执行对应的afterPropertiesSet方法,该方法中中有一个initRouterFunctions的动作,该动作会提取出IOC容器中注册的所有RouterFunction对象,并收集为一个List,之后借助Stream的reduce方法去调用RouterFunction的andOther方法,该所有RouterFunction组合为一个RouterFunction对象。

通过Debug可以发现,提取出的RouterFunction对象List集合只有1个元素,就是示例项目中自定义的UserRouterConfig。

RouterFunction对象List集合
DispatcherHandler的handle方法中,会调用RouterFunctionMapping的getHandler方法,getHandler方法执行完后,获取的Handler可以定位到UserRouterConfig。

获取的Handler

13.5.2 HandlerAdapter的不同

HandlerMapping的工作处理完成后,下面是HandlerAdapter执行Handler。即DispatcherHandler的handle方法的.flatMap(handler -> invokeHandler(exchange, handler))部分。

源码12HandlerFunctionAdapter.java

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

由 源码12 可知,invokeHandler方法会从所有HandlerAdapter中筛选出能够处理当前请求的HandlerAdapter。

在函数式端点开发中,底层使用的HandlerAdapter是HandlerFunctionAdapter

通过Dubug也可以发现底层确实选择了HandlerFunctionAdapter。

底层选择了HandlerFunctionAdapter

源码13HandlerFunctionAdapter.java

@Override
public boolean supports(Object handler) {
    return handler instanceof HandlerFunction;
}

@Override
public ModelAndView handle(HttpServletRequest servletRequest,
                           HttpServletResponse servletResponse,
                           Object handler) throws Exception {
    HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
    ServerRequest serverRequest = getServerRequest(servletRequest);
    ServerResponse serverResponse = handlerFunction.handle(serverRequest);
    return serverResponse.writeTo(servletRequest, servletResponse,
            new ServerRequestContext(serverRequest));
}

由 源码13 可知,HandlerFunctionAdapter判断是否可以处理当前请求的逻辑在supports方法,仅判断当前请求对应的Handler对象的类型是否是HandlerFunction即可。

在HandlerFunctionAdapter的handle方法中,会调用HandlerFunction的handle方法真正执行Handler,最终返回一个ModelAndView对象。

13.5.3 返回值处理的不同

HandlerAdapter的工作处理完成后,下面是ResultHandler对返回值进行处理。即DispatcherHandler的handle方法的.flatMap(result -> handleResult(exchange, result))部分。

和传统方式一样,在handleResult方法中,调用getResultHandler方法获取可以处理当前请求返回值的ResultHandler,再调用该ResultHandler的handleResult方法真正对返回值进行处理。

在函数式端点开发中,底层使用的ResultHandler是ServerResponseResultHandler。由Debug也可以发现这一点:

底层使用ServerResponseResultHandler

源码14ServerResponseResultHandler.java

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

由 源码14 可知,handleResult方法会从HandlerResult中取出ServerResponse对象,并将其写入ServerWebExchange中。

返回值处理完成后,一次完整的请求处理流程结束。

13.5.4 工作流程小结

基于函数式端点开发的工作流程如下:

基于函数式端点开发的工作流程

13.6 小结

第13章到此就梳理完毕了,本章的主题是:SpringBoot整合WebFlux。回顾一下本章的梳理的内容:

(三十八)SpringBoot整合WebWebFlux(一)WebFlux的自动装配
(三十九)SpringBoot整合WebWebFlux(二)DispatcherHandler的传统方式和函数式端点工作原理

更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

第14章主要梳理:运行SpringBoot应用。主要内容包括:

  • SpringBoot应用的项目打包方式;
  • 基于jar包独立运行的核心底层解析;
  • 基于war包运行的引导机制解析;
  • SpringBoot的优雅停机。
  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰色孤星A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值