快速使用WebFlux

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

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

1. WebFlux环境下用WebMvc风格

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

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

@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 ,能正常响应 "test" 字符串,证明 WebFlux 可以完美兼容 WebMvc 的开发风格

2. 逐步过渡到WebFlux

在上一篇咱看到了,Reactor 中的核心数据的封装是 FluxMono,那下面咱来改造上面的 DemoController ,用上这两个组件:

@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

@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 来配置路由规则(注意这是一个配置类):

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…

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

  • SpringWebMvc 基于原生 Servlet,所以它是命令式编程,编写相对熟悉,而且很方便调试;Servlet 可以是阻塞的,它更适合跟传统的关系型数据库等阻塞IO的组件进行交互。

  • SpringWebFlux 基于 Reactor,它是异步非阻塞的,它使用函数式编程,相较于命令式编程和声明式映射更灵活,而且它可以运行在 Netty 上,当然它也可以运行在 Tomcat 、Jetty 、Undertow 等基于 Servlet3.1 规范及以上的Web容器中。

  • SpringWebMvc 和 SpringWebFlux 都可以使用声明式注解编程来配置控制器和映射路径。

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

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

最后更新于