[TOC] ## `DispatcherHandler`介绍 与`Spring MVC`类似,`Spring WebFlux`是围绕前端控制器模式设计的,其中一个中心`WebHandler` `DispatcherHandler`为请求处理提供了一个共享算法,而实际的工作是由可配置的委托组件执行的。这个模型是灵活的,并支持不同的工作流。 `DispatcherHandler`从`Spring`配置中发现它需要的委托组件。它本身也被设计成一个`Spring Bean`,并实现了`ApplicationContextAware`来访问它运行的上下文。如果`DispatcherHandler`用一个`Bean`名`webHandler`声明,那么它会被`WebHttpHandlerBuilder`发现,后者将请求处理链放在一起。 `WebFlux`应用中的`Spring`配置通常包括: * `DispatcherHandler Bean` 名称为`webHandler` * `WebFilter`和`WebExceptionHandler` `Bean` * `DispatcherHandler`特殊`Bean` * 其它 提供配置给`WebHttpHandlerBuilder`来构建处理链,如下面的示例所示 ~~~ ApplicationContext context = ... WebHttpHandlerBuilder.applicationContext(context).filter(...).webHandler(...).exceptionHandler(...) .sessionManager(...).codecConfigurer(...).localeContextResolver(...).forwardedHeaderTransformer(...); ~~~ ## `DispatcherHandler`特殊的`Bean`类型 `DispatcherHandler`委托给特殊的`bean`来处理请求和呈现响应。所谓“`特殊bean`”,我们指的是实现`WebFlux`框架契约的`Spring`管理对象实例。它们通常带有内置契约,可以自定义、扩展或替换它们。 | Bean | 说明 | | --- | --- | | `HandlerMapping` | 将请求映射到处理程序。映射基于一些标准,这些标准的细节因`HandlerMapping`实现而不同——带注解的`Controller`、简单的`URL`模式映射等等。主要的`HandlerMapping`实现是`@RequestMapping`带注解方法的`RequestMappingHandlerMapping` ,`RouterFunctionMapping`用于功能性端点路由,`SimpleUrlHandlerMapping`用于显式注册`URI`路径模式和`WebHandler`实例。| |`HandlerAdapter`|帮助`DispatcherHandler`调用映射到请求的处理程序,而不管该处理程序实际是如何调用的。例如,调用带注解的控制器需要解析注解。`HandlerAdapter`的主要目的是保护`DispatcherHandler`不受此类细节的影响。| |`HandlerResultHandler`|处理`处理程序`调用的结果并完成响应| ## 请求处理流程 DispatcherHandler按如下方式处理请求: 1.要求每个HandlerMapping查找匹配的处理程序,并使用第一个匹配。 2.如果找到了处理程序,则通过适当的HandlerAdapter运行它,它将执行的返回值转换成HandlerResult。 3.HandlerResult被赋予适当的HandlerResultHandler,通过直接写入响应或使用视图来呈现来完成处理。 ## `HandlerMapping` 接口,由定义请求和处理程序对象之间映射的对象实现。默认实现类图如下: ![](https://img.kancloud.cn/cd/e4/cde4b9b127044f4d6f8041367ff529a3_1516x470.png) | Bean | 说明 | | --- | --- | |SimpleUrlHandlerMapping|从`URL`映射到请求处理程序`Bean`。支持映射到Bean实例和映射到Bean名称;后者对于非单例处理程序是必需的。例如:/welcome.html=ticketController| | RequestMappingHandlerMapping | RequestMappingInfoHandlerMapping的扩展,从类级和方法级的@RequestMapping注解中创建RequestMappingInfo实例。 | |RouterFunctionMapping|支持RouterFunctions的HandlerMapping实现。如果在构造时没有提供RouterFunction,这个映射将检测应用程序上下文中所有的路由器函数,并按顺序查询它们。| ## HandlerAdapter 它将`DispatcherHandler`从调用处理程序的细节中解耦,并使支持任何处理程序类型成为可能。 ![](https://img.kancloud.cn/7a/b8/7ab895ced004d858a98e51711305418d_1478x278.png) | Bean | 说明 | | --- | --- | | SimpleHandlerAdapter | 允许使用普通WebHandler契约的HandlerAdapter | |HandlerFunctionAdapter|支持HandlerFunctions的HandlerAdapter实现| |RequestMappingHandlerAdapter|支持@RequestMapping处理程序方法的调用| |WebSocketHandlerAdapter|允许WebSocketHandler类型的处理程序,这些处理程序通过SimpleUrlHandlerMapping映射到URL模式| ## HandlerResultHandler ![](https://img.kancloud.cn/cc/18/cc18f8621a26d4771aaff319da8d38c7_1550x260.png) 处理程序调用的返回值通过`HandlerAdapter`被包装为`HandlerResult`和一些附加上下文,并传递给声称支持它的第一个`HandlerResultHandler`。下表显示了可用的`HandlerResultHandler`实现,所有这些实现都在`WebFlux`配置中声明: | | | | | --- | --- |--- | | ResponseEntityResultHandler | 处理HttpEntity和ResponseEntity返回值。 | 0 | | ServerResponseResultHandler | 支持ServerResponses的HandlerResultHandler实现,通常用于功能端点 | 0 | | ResponseBodyResultHandler | 处理@ResponseBody方法或@RestController类的返回值。 | 100| | ViewResolutionResultHandler | CharSequence、View、Model、Map、Rendering或任何其他对象都被视为模型属性。 | Integer.MAX\_VALUE | </br> ## 视图解析 视图解析器允许使用`HTML`模板和模型向浏览器呈现,而无需将你绑定到特定的视图技术。在`Spring WebFlux`中,视图解析是通过一个专用的`HandlerResultHandler`来支持的,该`HandlerResultHandler`使用`ViewResolver`实例来映射一个`String`(代表一个逻辑视图名)到一个`View`实例,然后使用视图来呈现响应。 `ViewResolutionResultHandler`封装了支持以下返回类型的视图解析算法: * Void 或空值 -- 默认视图名 * String -- 视图名,除了@ModelAttribute注解 * View -- 要渲染的视图 * Model -- 属性添加到模型 * Map -- 属性添加到模型 * Rendering -- 用例驱动的API用于视图解析 * @ModelAttribute -- 模型的属性 * 复杂的值 -- 模型的属性 ### 处理 传递给`ViewResolutionResultHandler`的`HandlerResult`包含来自处理程序的返回值和包含在请求处理期间添加的属性的模型。返回值的处理方式如下: * `String`,`CharSequence`: 通过配置的ViewResolver实现列表解析为view的逻辑视图名。 * `void`:根据请求路径选择一个默认的视图名,移除前和尾的斜杠,并将其解析为一个视图。当没有提供视图名(例如,返回模型属性)或异步返回值(例如,`Mono<Void>`)时,也会发生相同的情况。 * `Rendering`:用例驱动的`API`用于视图解析。 * `Model`,`Map`:请求向模型中添加的额外属性。 * 其它:任何其他返回值(除了由`BeanUtils#isSimpleProperty`确定的简单类型)都被视为添加到模型中的模型属性。属性名是通过使用约定从类名派生出来的,除非存在处理程序方法`@ModelAttribute`注解。 模型可以包含异步的、反应性的类型(例如,来自`Reactor`或`RxJava`)。在呈现之前,`AbstractView`将这些模型属性解析为具体的值并更新模型。单值响应类型解析为单个值或没有值(如果为空),而多值响应类型(例如`Flux<T>`)被收集并解析为`List<T>`。 配置视图解析器很简单,只需向`Spring`配置中添加一个`ViewResolutionResultHandler Bean`。 ### 重定向 视图名中的特殊`redirect:`前缀允许你执行重定向。`UrlBasedViewResolver`(及其子类)将此识别为需要重定向的指令。视图名的其余部分是重定向`URL`。 实际效果与控制器返回`RedirectView`或`render.redirectto ("abc").build()`相同,但现在控制器本身可以根据逻辑视图名称进行操作。像`redirect:/some/resource`这样的视图名是相对于当前应用程序的,而像`redirect:https://example.com/arbitrary/path`这样的视图名则重定向到绝对`URL`。 ### 异常 从`HandlerAdapter`返回的`HandlerResult`可以公开一个基于特定于处理程序的机制进行错误处理的函数。这个错误函数在以下情况被调用: * 处理程序(例如`@Controller`)调用失败。 * 通过`HandlerResultHandler`对处理程序返回值的处理失败。 只要错误信号发生在处理程序返回的反应式式类型产生任何数据项之前,`error`函数可以更改响应(例如,变为错误状态) ### 内容协商 `ViewResolutionResultHandler`支持内容协商。它将请求媒体类型与每个所选视图支持的媒体类型进行比较。使用支持请求的媒体类型的第一个视图。 为了支持`JSON`和`XML`等媒体类型,`Spring WebFlux`提供了`HttpMessageWriterView`,这是一个通过`HttpMessageWriter`呈现的特殊视图。通常,你可以通过`WebFlux Configuration`将这些视图配置为默认视图。如果与请求的媒体类型匹配,则始终选择并使用默认视图。