# DispatcherHandler的工作原理（传统方式）

咱前面也看到了，也做过示例，咱知道 WebFlux 可以完美兼容 WebMvc 的开发风格，那自然咱就猜测，WebFlux 中的核心前端控制器 `DispatcherHandler` 估计能跟 `DispatcherServlet` 在这部分的处理上差不多，抱着这个猜测，咱来以Debug的方式来观察处理逻辑走向。

## 1. DispatcherHandler#handle

```java
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` ，猜想它应该是类似于这两个类的合体：

```java
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` ：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673185874878-9f297a8f-325e-489a-8189-d36e0f1de25c.png)

之后的return结构中是一连串链式调用，这也体现出了函数式编程和响应式编程的一个弊端：Debug真的困难。我在最开始Debug找线索时，真是费了不少功夫，才慢慢找到这些端倪。

这段调用中可读性倒是挺高，咱参考之前 `DispatcherServlet` 的工作原理，大概分析一下这里面的三个步骤：

## 2. 选定HandlerMapping

不难看出，选定 `HandlerMapping` 的步骤是这几步：

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

首先进入 `formInterable` 方法：

### 2.1 Flux#fromIterable

```java
public static <T> Flux<T> fromIterable(Iterable<? extends T> it) {
    return onAssembly(new FluxIterable<>(it));
}
```

它将这一组 `HandlerMapping` 转换成了一组 `FluxIterable` ，之后传入了 `onAssembly` 方法中。

### 2.2 onAssembly

```java
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 流。

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673185921357-b9d42c18-3480-47ae-b209-4c8e438eef88.png)

### 2.3 concatMap(mapping -> mapping.getHandler(exchange))

上面将那一组 `HandlerMapping` 转换为 Flux后，接下来要从这组 `HandlerMapping` 中找出能处理当前请求的 `HandlerMapping` 了：

来到 `AbstractHandlerMapping#getHandler` ：

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

这个方法分为两部分：先执行 `getHandlerInternal` 获取可以处理当前请求的 `HandlerMapping` ，再执行 `map` 方法进行后处理。

#### 2.3.1 getHanderInternal

对于传统的 `@RequestMapping` 方式标注的 “Handler”，会进入 `AbstractHandlerMethodMapping#getHandlerInternal` 方法（又跟 WebMvc 非常相似）：

```java
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` ：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673185957637-5943acdd-dd4c-446e-b500-e6ee228c2981.png)

它已经找到了我写的 `DemoController` 。

#### 2.3.2 搜索到后的map

```java
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

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

由于是 WebMvc 开发风格的处理，返回的 `HandlerMethod` 也是 `DemoController` 里的，默认情况下有3个 HandlerAdapter ：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673185986357-143b4ac8-1d6e-4665-b84b-d38de94dd111.png)

不用想，肯定走 `RequestMappingHandlerAdapter` ，通过Debug发现确实如此：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673186005974-76fccf3c-53c6-41a7-bba6-c81ce1e7b1da.png)

之后直接调了它的 `handle` 方法了！

## 4. 【目标方法】HandlerAdapter#handle

来到 `RequestMappingHandlerAdapter` 中：

```java
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中根本没有收录进去，而且也找不到任何相关的资料，只能靠源码的部分来大概看一眼它：

```java
/** 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 执行目标方法，处理返回值和异常

```java
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` 中：

```java
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方法反射调用

```java
// 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` 方法，要进行返回值处理：

```java
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` 也都获取到了：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673186100529-d7ef6fcf-458c-4876-8d40-7f2512610585.png)

多提一嘴，`ReactiveAdapter` 就是通过上一篇咱看自动装配中看到的 `ReactiveAdapterRegistry` 组件获取而来，用它来实际兼容 RxJava 等其他响应式编程框架。

最后，它会将返回值封装为一个 `HandlerResult` 对象，返回出去。

## 5. 最后的返回值处理

回到 `DispatcherHandler` 中：

```java
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` ，下面还要再处理上一步的返回值：

```java
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

```java
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` ：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673186153791-45502917-a675-436c-ace2-e355ad962a9e.png)

很容易判断，通过 `@RestController` 出来的方法，都会走 `ResponseBodyResultHandler` ：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673186170688-de907de5-7770-490f-b5d1-da69ab826de8.png)

### 5.2 ResponseBodyResultHandler#handleResult

```java
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. 流程图

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673186193550-b5643d7a-65a2-4f9e-965f-2b13b1b6d4b9.png)

## 7. 小结

1. `DispatcherHandler` 与 `DispatcherServlet` 的处理思路几乎完全相同，包括寻找 `HandlerMapping` 、`HandlerAdapter` 、执行目标方法等。
2. `DispatcherHandler` 相比较 `DispatcherServlet` 最大的不同点，是里面的实现绝大部分都采用响应式流编程。
