484 lines
25 KiB
Markdown
484 lines
25 KiB
Markdown
- [Spring Webflux](#spring-webflux)
|
||
- [Concept](#concept)
|
||
- [核心机制](#核心机制)
|
||
- [reactive](#reactive)
|
||
- [back pressure](#back-pressure)
|
||
- [reactive stream](#reactive-stream)
|
||
- [编程模型](#编程模型)
|
||
- [并发模型](#并发模型)
|
||
- [spring mvc](#spring-mvc)
|
||
- [webflux](#webflux)
|
||
- [调用阻塞api](#调用阻塞api)
|
||
- [Reactive Core](#reactive-core)
|
||
- [HttpHandler](#httphandler)
|
||
- [server api adapters](#server-api-adapters)
|
||
- [Reactor Netty](#reactor-netty)
|
||
- [Undertow](#undertow)
|
||
- [Tomcat](#tomcat)
|
||
- [Jetty](#jetty)
|
||
- [WebHandler API](#webhandler-api)
|
||
- [bean types for WebHttpHandlerBuilder auto-detect](#bean-types-for-webhttphandlerbuilder-auto-detect)
|
||
- [Form Data](#form-data)
|
||
- [Multipart Data](#multipart-data)
|
||
- [Filter](#filter)
|
||
- [UrlHandler](#urlhandler)
|
||
- [Exceptions](#exceptions)
|
||
- [Logging](#logging)
|
||
- [Log Id](#log-id)
|
||
- [Appenders](#appenders)
|
||
- [DispatcherHandler](#dispatcherhandler)
|
||
- [DispatcherHandler委托](#dispatcherhandler委托)
|
||
- [Processing](#processing)
|
||
- [Result Handling](#result-handling)
|
||
- [Annotated Controllers](#annotated-controllers)
|
||
- [@Controller](#controller)
|
||
- [AOP](#aop)
|
||
|
||
|
||
# Spring Webflux
|
||
## Concept
|
||
### 核心机制
|
||
#### reactive
|
||
reactive代表基于“事件响应”的编程模型,
|
||
#### back pressure
|
||
在spring webflux中,`back pressure`为反应式编程的核心机制,用于协调生产者和消费者之间的速率差异,令系统在高负载或资源受限的情况下仍能稳定运行。
|
||
|
||
- 同步场景:在同步场景下,阻塞式调用是一种天然的back pressure形式,调用方会阻塞并等待,直到被调用方执行完成
|
||
- 非阻塞场景:在非阻塞的代码中,需要关注事件速率,生产者产生事件的速率不能压过消费者消费的速率
|
||
|
||
##### reactive stream
|
||
reactive stream为一个小型规范,定义了异步组件和back pressure交互的规范。reactive stream的主要用途是让Subscriber控制publisher产生数据的速率。
|
||
|
||
### 编程模型
|
||
`spring-web` module包含Spring webflux的响应式基础,包括若夏内容:
|
||
- http抽象
|
||
- 对支持server的reactive stream adapters
|
||
- codecs
|
||
- 核心WebHandler API,其与servlet api兼容
|
||
|
||
Spring webflux提供了如下两种编程模型:
|
||
- `Annotated Controllers`: 和spring mvc一致,基于相同的注解。`spring mvc`和`webflux controllers`都支持`reactive return type`,因此,很难区分`spring mvc`和`webflux controllers`
|
||
- `Functional Endpoint`:基于lambda的、轻量的函数编程模型。其可以被看做是一个`支持对请求进行route和handle`的工具集合。
|
||
|
||
### 并发模型
|
||
#### spring mvc
|
||
在`spring mvc`(通常为servlet应用)中,其假设当前线程可能会被阻塞(例如,远程调用等会阻塞当前线程)。为了减少处理请求时阻塞所带来的影响,servlet容器会使用包含大量线程的线程池。
|
||
|
||
#### webflux
|
||
对于`webflux`(通常为non-blocking server),其会假设应用并不会阻塞,故而,非阻塞的server可以使用一个线程数较少且固定的线程池(event loop workers)来处理请求。
|
||
|
||
#### 调用阻塞api
|
||
当想要在webflux中使用阻塞api时,可以按如下方式进行使用。`RxJava`和`Reactive`都支持`publushOn`操作,其支持在另一个线程中`继续执行`。
|
||
|
||
## Reactive Core
|
||
`spring-web`对于reactive web应用具有如下支持:
|
||
- 对于server request的处理,拥有如下两种级别的支持:
|
||
- `HttpHandler`: 基于`non-blocking I/O`和`Reactive Stream back pressure`,适配`Reactor Netty, Undertow, Tomcat, Jetty以及任一Servlet Container
|
||
- `WebHandler`:稍高级别的、通用的web api,用于请求处理,在此基础上构建基于`annotated controllers`和`functional endpoints`的编程模型
|
||
- 对于client端,`ClientHttpConnector`用于发送http请求,同时兼容`非阻塞io和Reactive Stream back pressure`
|
||
- codecs用于客户端和服务端的序列化和反序列化
|
||
|
||
### HttpHandler
|
||
HttpHandler中只通过一个单独的方法来处理请求和相应,其对不同的http server api进行了最小化的抽象。
|
||
|
||
下表描述了支持的api:
|
||
<table class="tableblock frame-all grid-all stripes-odd stretch">
|
||
<colgroup>
|
||
<col style="width: 20%;">
|
||
<col style="width: 40%;">
|
||
<col style="width: 40%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">Server name</th>
|
||
<th class="tableblock halign-left valign-top">Server API used</th>
|
||
<th class="tableblock halign-left valign-top">Reactive Streams support</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Netty</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Netty API</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/reactor/reactor-netty" class="external" target="_blank">Reactor Netty</a></p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Undertow</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Undertow API</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Undertow to Reactive Streams bridge</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Tomcat</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[]</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Servlet non-blocking I/O to Reactive Streams bridge</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Jetty</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet non-blocking I/O; Jetty API to write ByteBuffers vs byte[]</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Servlet non-blocking I/O to Reactive Streams bridge</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet container</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Servlet non-blocking I/O</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">spring-web: Servlet non-blocking I/O to Reactive Streams bridge</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
下表中描述了server依赖:
|
||
<table class="tableblock frame-all grid-all stripes-odd stretch">
|
||
<colgroup>
|
||
<col style="width: 33.3333%;">
|
||
<col style="width: 33.3333%;">
|
||
<col style="width: 33.3334%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">Server name</th>
|
||
<th class="tableblock halign-left valign-top">Group id</th>
|
||
<th class="tableblock halign-left valign-top">Artifact name</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Reactor Netty</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">io.projectreactor.netty</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">reactor-netty</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Undertow</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">io.undertow</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">undertow-core</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Tomcat</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">org.apache.tomcat.embed</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">tomcat-embed-core</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Jetty</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">org.eclipse.jetty</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">jetty-server, jetty-servlet</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
#### server api adapters
|
||
如下示例展示了如何使用`HttpHandler` Adapters来适配不同的Server Api:
|
||
|
||
##### Reactor Netty
|
||
```java
|
||
HttpHandler handler = ...
|
||
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
|
||
HttpServer.create().host(host).port(port).handle(adapter).bindNow();
|
||
```
|
||
|
||
##### Undertow
|
||
```java
|
||
HttpHandler handler = ...
|
||
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
|
||
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
|
||
server.start();
|
||
```
|
||
|
||
##### Tomcat
|
||
```java
|
||
HttpHandler handler = ...
|
||
Servlet servlet = new TomcatHttpHandlerAdapter(handler);
|
||
|
||
Tomcat server = new Tomcat();
|
||
File base = new File(System.getProperty("java.io.tmpdir"));
|
||
Context rootContext = server.addContext("", base.getAbsolutePath());
|
||
Tomcat.addServlet(rootContext, "main", servlet);
|
||
rootContext.addServletMappingDecoded("/", "main");
|
||
server.setHost(host);
|
||
server.setPort(port);
|
||
server.start();
|
||
```
|
||
|
||
##### Jetty
|
||
```java
|
||
HttpHandler handler = ...
|
||
Servlet servlet = new JettyHttpHandlerAdapter(handler);
|
||
|
||
Server server = new Server();
|
||
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
|
||
contextHandler.addServlet(new ServletHolder(servlet), "/");
|
||
contextHandler.start();
|
||
|
||
ServerConnector connector = new ServerConnector(server);
|
||
connector.setHost(host);
|
||
connector.setPort(port);
|
||
server.addConnector(connector);
|
||
server.start();
|
||
```
|
||
|
||
### WebHandler API
|
||
`org.springframework.web.server` package基于`HttpHandler`构建,提供了通用的Web API。Web Api由多个`WebException`, 多个`WebFilter`, 一个`WebHandler`组件构成,组成了一个chain。
|
||
|
||
相比于`HttpHandler`仅仅是对不同http server的抽象,`WebHandler`提供了一个更加通用、更加广泛的功能集合:
|
||
- user sessions attributes
|
||
- request attributes
|
||
- resolved `Locale` or `Principal` for request
|
||
- abstractions for multipart data
|
||
|
||
#### bean types for WebHttpHandlerBuilder auto-detect
|
||
在spring上下文中,`WebHttpHandlerBuilder`可以自动探测到如下类型的components:
|
||
<table class="tableblock frame-all grid-all stripes-odd stretch">
|
||
<colgroup>
|
||
<col style="width: 25%;">
|
||
<col style="width: 25%;">
|
||
<col style="width: 12.5%;">
|
||
<col style="width: 37.5%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">Bean name</th>
|
||
<th class="tableblock halign-left valign-top">Bean type</th>
|
||
<th class="tableblock halign-left valign-top">Count</th>
|
||
<th class="tableblock halign-left valign-top">Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><any></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>WebExceptionHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0..N</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Provide handling for exceptions from the chain of <code>WebFilter</code> instances and the target
|
||
<code>WebHandler</code>. For more details, see <a href="#webflux-exception-handler">Exceptions</a>.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><any></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>WebFilter</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0..N</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Apply interception style logic to before and after the rest of the filter chain and
|
||
the target <code>WebHandler</code>. For more details, see <a href="#webflux-filters">Filters</a>.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>webHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>WebHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">1</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The handler for the request.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>webSessionManager</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>WebSessionManager</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0..1</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The manager for <code>WebSession</code> instances exposed through a method on <code>ServerWebExchange</code>.
|
||
<code>DefaultWebSessionManager</code> by default.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>serverCodecConfigurer</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ServerCodecConfigurer</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0..1</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">For access to <code>HttpMessageReader</code> instances for parsing form data and multipart data that is then
|
||
exposed through methods on <code>ServerWebExchange</code>. <code>ServerCodecConfigurer.create()</code> by default.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>localeContextResolver</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>LocaleContextResolver</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0..1</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">The resolver for <code>LocaleContext</code> exposed through a method on <code>ServerWebExchange</code>.
|
||
<code>AcceptHeaderLocaleContextResolver</code> by default.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>forwardedHeaderTransformer</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ForwardedHeaderTransformer</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0..1</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">For processing forwarded type headers, either by extracting and removing them or by removing them only.
|
||
Not used by default.</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
#### Form Data
|
||
`ServerWebExchange`将会向外暴露如下方法,用于访问form data:
|
||
```java
|
||
Mono<MultiValueMap<String, String>> getFormData();
|
||
```
|
||
`DefaultServerWebExchange`使用`HttpMessageReader`来对form data(application/x-www-form-urlencoded)进行parse操作,将formdata转化为`MultiValueMap`。
|
||
|
||
#### Multipart Data
|
||
`ServerWebExchange`向外暴露如下方法,用于访问multipart data。
|
||
|
||
```java
|
||
Mono<MultiValueMap<String, Part>> getMultipartData();
|
||
```
|
||
|
||
`DefaultServerWebExchange`将会使用`HttpMessageReader<MultiValueMap<String, Part>>`来对`multipart/form-data`,`multipart/mixed`,`multipart/related`数据进行转换,数据将会被转化为`MultiValueMap`类型。
|
||
|
||
#### Filter
|
||
在`WebHandler API`中,可以使用`WebFilter`来实现拦截式的逻辑,当使用`Webflux Config`时,`WebFilter`的注可以通过将其注册为bean来实现。
|
||
|
||
对于`WebFilter`的优先级,欸可以通过使用`@Order`注解或实现`Ordered`接口来实现。
|
||
|
||
#### UrlHandler
|
||
在编写web程序时,可能希望controller endpoint既能够匹配末尾带`/`的url版本,又能匹配末尾不带`/`的url版本。
|
||
|
||
> 例如,想要让`@GetMapping("/home")`既能够匹配`GET /home`,又能够匹配`GET /home/`。
|
||
|
||
对此,可以使用`UrlHandlerFilter`进行处理,其支持如下两种配置:
|
||
- 当接收到带有末尾带有`/`的url时,向浏览器返回一个重定向状态,让浏览器重新请求末尾不带`/`的url
|
||
- 将请求当作不带末尾`/`,并对请求继续处理
|
||
|
||
实例化UrlHandlerFilter的示例如下:
|
||
```java
|
||
UrlHandlerFilter urlHandlerFilter = UrlHandlerFilter
|
||
// will HTTP 308 redirect "/blog/my-blog-post/" -> "/blog/my-blog-post"
|
||
.trailingSlashHandler("/blog/**").redirect(HttpStatus.PERMANENT_REDIRECT)
|
||
// will mutate the request to "/admin/user/account/" and make it as "/admin/user/account"
|
||
.trailingSlashHandler("/admin/**").mutateRequest()
|
||
.build();
|
||
```
|
||
|
||
#### Exceptions
|
||
在`WebHandler Api`中,可以使用`WebExceptionHandler`对异常进行处理。当使用`Webflux Config`时,注册`WebExceptionHandler`仅需将其声明为bean即可,可以通过`@Order`注解或`Ordered`接口来指明顺序。
|
||
|
||
#### Logging
|
||
##### Log Id
|
||
在webflux中,同一个请求可能在多个线程中被执行过,故而,在定位请求日志时,无法通过线程id来关联请求。为了解决该问题,spring webflux在打印消息时前缀了一个`log id`,其是针对单个特定请求的。
|
||
|
||
在server端,log id存储在`ServerWebExchange`中的`LOG_ID_ATTRIBUTE`属性中。在client端,log id存储在`Client Request`中的`LOG_ID_ATTRIBUTE`属性中。
|
||
|
||
##### Appenders
|
||
`slf4j`和`log4j`等日志库都提供异步logger,用于避免阻塞。使用异步logger会存在部分缺点,例如`丢弃无法排队的异步消息`。但是,其仍然是目前非阻塞框架的最佳选择。
|
||
|
||
## DispatcherHandler
|
||
和spring MVC类似,spring webflux同样按照`font controller pattern`进行设计,包含一个`central WebHandler`,即`DispatcherHandler`。
|
||
|
||
Dispathcer提供了一个共享的请求处理算法,将实际的处理逻辑委托给其他可配置的组件。
|
||
|
||
`DispatcherHandler`通过spring配置来发现委托的组件。`DispatcherHandler`其本身也是一个spring bean,并且实现了`ApplicationContextAware`接口,可以访问spring上下文。
|
||
|
||
如果`Dispatcher`的bean name为`webHandler`,那么其会被`WebHttpHandlerBuilder`发现,并且将其放入`request-processing chain`中。
|
||
|
||
webflux应用中包含的spring configuration通常包括:
|
||
- bean name为`webHandler`的`DispatcherHandler`
|
||
- `WebFilter`和`WebExceptionHandler` beans
|
||
- 被委托给`webHandler`的beans
|
||
- 其他
|
||
|
||
上述配置将被被`WebHttpHandlerBuilder`使用,用于构建process chain,示例如下所示:
|
||
```java
|
||
ApplicationContext context = ...
|
||
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build();
|
||
```
|
||
上述示例中返回的handler可以和`server adapter`结合使用。
|
||
|
||
### DispatcherHandler委托
|
||
`DispatcherHandler`会将请求的委托给特定的bean对象,bean对象会处理请求并且将结果渲染到response中。
|
||
|
||
DispatcherHandler会对如下类型的bean进行auto-detect。
|
||
|
||
<table id="webflux-special-beans-table" class="tableblock frame-all grid-all stripes-odd stretch">
|
||
<colgroup>
|
||
<col style="width: 33.3333%;">
|
||
<col style="width: 66.6667%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">Bean type</th>
|
||
<th class="tableblock halign-left valign-top">Explanation</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>HandlerMapping</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Map a request to a handler. The mapping is based on some criteria, the details of
|
||
which vary by <code>HandlerMapping</code> implementation — annotated controllers, simple
|
||
URL pattern mappings, and others.</p>
|
||
<p class="tableblock"> The main <code>HandlerMapping</code> implementations are <code>RequestMappingHandlerMapping</code> for
|
||
<code>@RequestMapping</code> annotated methods, <code>RouterFunctionMapping</code> for functional endpoint
|
||
routes, and <code>SimpleUrlHandlerMapping</code> for explicit registrations of URI path patterns
|
||
and <code>WebHandler</code> instances.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>HandlerAdapter</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Help the <code>DispatcherHandler</code> to invoke a handler mapped to a request regardless of
|
||
how the handler is actually invoked. For example, invoking an annotated controller
|
||
requires resolving annotations. The main purpose of a <code>HandlerAdapter</code> is to shield the
|
||
<code>DispatcherHandler</code> from such details.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>HandlerResultHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Process the result from the handler invocation and finalize the response.
|
||
See <a href="#webflux-resulthandling">Result Handling</a>.</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
### Processing
|
||
`DispatcherHandler`按照如下方式处理请求:
|
||
- 首先,会挨个查询`HandlerMapping`用于查找匹配的handler,会匹配第一个匹配的handler
|
||
- 如果匹配到handler,会通过对应的HandlerAdapter执行该handler,并且会将handler返回的值作为`HandlerResult`暴露
|
||
- `HandlerResult`将会被分配给指定的`HandlerResultHandler`进行处理,可能会直接向response中写入数据或渲染view
|
||
|
||
### Result Handling
|
||
通过`HandlerAdapter`对handler进行调用,返回的结果会被包裹在`HandlerResult`中,随之还包含额外的上下文。
|
||
|
||
`HandlerResult`将会被传递给第一个支持其的上下文,下表中展示了`HandlerResultHandler`的实现:
|
||
|
||
<table class="tableblock frame-all grid-all stripes-odd stretch">
|
||
<colgroup>
|
||
<col style="width: 25%;">
|
||
<col style="width: 50%;">
|
||
<col style="width: 25%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">Result Handler Type</th>
|
||
<th class="tableblock halign-left valign-top">Return Values</th>
|
||
<th class="tableblock halign-left valign-top">Default Order</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ResponseEntityResultHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ResponseEntity</code>, typically from <code>@Controller</code> instances.</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ServerResponseResultHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ServerResponse</code>, typically from functional endpoints.</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">0</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ResponseBodyResultHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Handle return values from <code>@ResponseBody</code> methods or <code>@RestController</code> classes.</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">100</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ViewResolutionResultHandler</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>CharSequence</code>, <a href="https://docs.spring.io/spring-framework/docs/6.2.3/javadoc-api/org/springframework/web/reactive/result/view/View.html"><code>View</code></a>,
|
||
<a href="https://docs.spring.io/spring-framework/docs/6.2.3/javadoc-api/org/springframework/ui/Model.html">Model</a>, <code>Map</code>,
|
||
<a href="https://docs.spring.io/spring-framework/docs/6.2.3/javadoc-api/org/springframework/web/reactive/result/view/Rendering.html">Rendering</a>,
|
||
or any other <code>Object</code> is treated as a model attribute.</p>
|
||
<p class="tableblock"> See also <a href="#webflux-viewresolution">View Resolution</a>.</p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>Integer.MAX_VALUE</code></p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
## Annotated Controllers
|
||
spring webflux提供了基于注解的编程模型,通过使用注解来向外暴露request mappings。通过注解标注的controllers可包含灵活的方法签名,并且无需继承任何基类或实现指定接口。
|
||
|
||
使用示例如下所示:
|
||
```java
|
||
@RestController
|
||
public class HelloController {
|
||
|
||
@GetMapping("/hello")
|
||
public String handle() {
|
||
return "Hello WebFlux";
|
||
}
|
||
}
|
||
```
|
||
|
||
### @Controller
|
||
在基于注解的webflux变成模型中,可以使用`@Controller`或`@RestController`来声明controller bean。
|
||
|
||
#### AOP
|
||
对于部分场景,需要在运行时使用aop proxy来对controller进行decorate。
|
||
|
||
对于针对controller的aop,推荐使用`class-based proxy`,可以使用`@EnableTransactionManagement(proxyTargetClass=true)`来实现。
|
||
|