# 快速使用WebFlux

前一篇咱快速的了解了响应式编程，以及 Reactor 框架的基本使用，接下来咱要开始正式接触 WebFlux 了。

咱都知道，自打 SpringFramework5 发行后，之前的 **SpringMVC** 被改名为 **SpringWebMvc**，因为它多了一个兄弟叫 **SpringWebFlux** 。而且 Spring 的开发者们为了避免咱们这些使用者因为新技术的门槛过高而吓跑，WebFlux 可以完美使用 WebMvc 的开发风格。下面咱先实战使用 WebFlux 框架，但还是用 WebMvc 的开发风格。

## 1. WebFlux环境下用WebMvc风格

导入的依赖还是咱上一篇提到的 `spring-boot-starter-webflux` ，没有引入 WebMvc 模块，直接运行主启动类，观察控制台的输出：

```yaml
1970-01-01 10:00:00.000  INFO 10224 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on DESKTOP with PID 10224 (D:\IDEA\spring-boot-demo\target\classes started by LinkedBear in D:\IDEA\spring-boot-demo)
1970-01-01 10:00:00.000  INFO 10224 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
1970-01-01 10:00:00.000  INFO 10224 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
1970-01-01 10:00:00.000  INFO 10224 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.941 seconds (JVM running for 4.619)
```

注意看应用启动在 **Netty** 上而不是 Tomcat 了！因为 Netty 更适合做响应式编程的应用服务器。

接下来，跟之前开发 WebMvc 应用一样，咱编写一个 `DemoController`：

```java
@RestController
public class DemoController {

    @GetMapping("/test")
    public String test() {
        return "test";
    }

    @GetMapping("/list")
    public List<Integer> list() {
        return Arrays.asList(1, 2, 3);
    }

}
```

之后用浏览器或者 postman 发送请求：[http://localhost:8080/test](https://link.juejin.cn/?target=http%3A%2F%2Flocalhost%3A8080%2Ftest) ，能正常响应 `"test"` 字符串，证明 **WebFlux 可以完美兼容 WebMvc 的开发风格**。

## 2. 逐步过渡到WebFlux

在上一篇咱看到了，Reactor 中的核心数据的封装是 `Flux` 和 `Mono`，那下面咱来改造上面的 `DemoController` ，用上这两个组件：

```java
@RestController
public class DemoController {

    @GetMapping("/test")
    public Mono<String> test() {
        return Mono.just("test");
    }

    @GetMapping("/list")
    public Flux<Integer> list() {
        return Flux.just(1, 2, 3);
    }

}
```

如果只返回一个对象，则使用 `Mono` 替换，返回列表则使用 `Flux` 替换。

但这样写的话，只是替换了返回值类型，注解还是原来 **WebMvc** 中的，SpringWebFlux 有自己的专门一套开发 `Controller` 层的API，那就是函数式开发。

## 3. WebFlux的函数式开发

在切换完全不同的开发风格之前，先大概想一下之前的 WebMvc 部分，关键点都有哪些：

* Controller 类上要打 `@Controller` 或者 `@RestController` 注解
* url映射的方法要在方法上打 `@RequestMapping` 注解或者它的扩展注解

咱之前分析 WebMvc 的原理时也知道，这些 `Controller` 中的方法，最终都会封装为一个一个的 `**Handler**` ，每个 `Handler` 都有自己匹配的 **url**。

由此，WebFlux 的函数式开发的核心点就转换为两种关键组件：`**HandlerFunction**` 和 `**RouterFunction**` 。

### 3.1 DemoController转换为DemoHandler

先将 WebMvc 中的 `DemoController` 转换为 WebFlux 中的 `DemoHandler`：

```kotlin
@Component
public class DemoHandler {
    
    public Mono<ServerResponse> test(ServerRequest request) {
        return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(Mono.just("test"), String.class);
    }
    
    public Mono<ServerResponse> list(ServerRequest request) {
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(Flux.just(1, 2, 3), Integer.class);
    }
    
}
```

可以发现，现在的 `DemoHandler` 已经变成了一个普通类，没有继承也没有实现，除了 `@Component` 注解外没有任何注解，IOC容器也不知道它到底是什么功能的Bean，也不知道这些方法是不是要转换为实际处理客户端请求的 `Handler` 。

### 3.2 新编写RouterConfiguration

因为上面的 `DemoHandler` 已经没有了 url 映射的功能，需要手动声明路由规则，下面咱编写一个 `RouterConfiguration` 来配置路由规则（注意这是一个配置类）：

```java
import static org.springframework.web.reactive.function.server.RequestPredicates.*;

@Configuration
public class RouterConfiguration {

    @Autowired
    private DemoHandler demoHandler;

    @Bean
    public RouterFunction<ServerResponse> demoRouter() {
        return RouterFunctions.route(GET("/test").and(accept(MediaType.TEXT_PLAIN)), demoHandler::test)
            .andRoute(GET("/list").and(accept(MediaType.APPLICATION_JSON)), demoHandler::list);
    }

}
```

可以发现这部分的映射是手动声明的。当然这部分代码可能比较难看懂，因为在上面静态导入了 `RequestPredicates` 中的所有静态属性和方法。

大概看一眼编写思路：它需要注入前面编写的 `DemoHandler` ，下面注册一些 `RouterFunction` 类型的Bean，注册的过程中需要引用 `DemoHandler` 中的方法，以及配置请求类型、uri、响应类型。

至此，最简单的 WebFlux 使用Demo演示完毕。

***

## 4. WebMvc与WebFlux的对比

写到这里，咱先不着急开始走原理，先停下来思考一下，WebMvc 和 WebFlux 有哪些是相同的，又有哪些是不同的呢？

官方文档中有一张图我觉得不错，这里引用一下吧：

[docs.spring.io/spring/docs…](https://link.juejin.cn/?target=https%3A%2F%2Fdocs.spring.io%2Fspring%2Fdocs%2F5.1.10.RELEASE%2Fspring-framework-reference%2Fweb-reactive.html%23webflux-framework-choice)

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673184875431-0225840a-7ced-474f-88ff-09b87c26b4b9.png)

这张图很直观的展示出了 WebMvc 和 WebFlux 的兼容功能点，以及各自领域目前特有的功能点。

* SpringWebMvc 基于原生 Servlet，所以它是命令式编程，编写相对熟悉，而且很方便调试；Servlet 可以是阻塞的，它更适合跟传统的关系型数据库等阻塞IO的组件进行交互。
* SpringWebFlux 基于 Reactor，它是异步非阻塞的，它使用函数式编程，相较于命令式编程和声明式映射更灵活，而且它可以运行在 Netty 上，当然它也可以运行在 Tomcat 、Jetty 、Undertow 等基于 Servlet3.1 规范及以上的Web容器中。
* SpringWebMvc 和 SpringWebFlux 都可以使用声明式注解编程来配置控制器和映射路径。

还有一张图，描述的差不多也是这个意思，也就是下面这张图：

![img](https://cdn.nlark.com/yuque/0/2023/png/229542/1673184908971-7d5a9d7a-206f-4044-ab70-e3150a3945c7.png)

【大概了解 WebFlux 的使用之后，下面咱开始解析 WebFlux 中的原理，并且咱希望通过源码分析和原理解读，来从更深的层面来对比 WebMvc和 WebFlux】


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-frameworks-90/spring/spring-webflux/kuai-su-shi-yong-webflux.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
