> For the complete documentation index, see [llms.txt](https://ldbmcs.gitbook.io/java/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ldbmcs.gitbook.io/java/java-frameworks-90/spring/spring-webflux/kuai-su-shi-yong-webflux.md).

# 快速使用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】
