14.SpringBoot学习(十四)——Spring Boot WebFlux初体验

摘要:
SpringWebFlux是SpringFramework5.0中引入的一个新的响应式Web框架。基于注释的模型与SpringMVC模型非常相似。WebFlux的适应与RxJava或其他动态库的使用相对应。有关详细信息,请参见反应式库。响应流在互操作性中发挥着重要作用。
1.简介

1.1 概述

Spring WebFlux is the new reactive web framework introduced in Spring Framework 5.0. Unlike Spring MVC, it does not require the Servlet API, is fully asynchronous and non-blocking, and implements the Reactive Streams specification through the Reactor project.

Spring WebFlux comes in two flavors: functional and annotation-based. The annotation-based one is quite close to the Spring MVC model, as shown in the following example:

Spring WebFlux 是 Spring Framework 5.0 中引入的新的响应式Web框架。与Spring MVC不同,它不需要Servlet API,是完全异步和非阻塞的,并且通过Reactor项目实现Reactive Streams规范。 Spring WebFlux有两种形式:功能性的和基于注释的。基于注释的模型非常类似于Spring MVC模型,如本文所示

1.2 特点

Reactive Streams plays an important role for interoperability. It is of interest to libraries and infrastructure components but less useful as an application API, because it is too low-level. Applications need a higher-level and richer, functional API to compose async logic — similar to the Java 8 Stream API but not only for collections. This is the role that reactive libraries play.

Reactor is the reactive library of choice for Spring WebFlux. It provides the Mono and Flux API types to work on data sequences of 0..1 (Mono) and 0..N (Flux) through a rich set of operators aligned with the ReactiveX vocabulary of operators. Reactor is a Reactive Streams library and, therefore, all of its operators support non-blocking back pressure. Reactor has a strong focus on server-side Java. It is developed in close collaboration with Spring.

WebFlux requires Reactor as a core dependency but it is interoperable with other reactive libraries via Reactive Streams. As a general rule, a WebFlux API accepts a plain Publisher as input, adapts it to a Reactor type internally, uses that, and returns either a Flux or a Mono as output. So, you can pass any Publisher as input and you can apply operations on the output, but you need to adapt the output for use with another reactive library. Whenever feasible (for example, annotated controllers), WebFlux adapts transparently to the use of RxJava or another reactive library. See Reactive Libraries for more details.

响应流对于互操作性起着重要作用。库和基础结构组件对此很感兴趣,但由于它太底层了,它作为应用程序API的用处不大。应用程序需要更高级别且功能更丰富的API来构成异步逻辑,这与Java 8 Stream API相似,但不仅适用于集合。这就是反应式库的作用。

Reactor是Spring WebFlux的首选反应库。它提供了Mono和Flux API类型,以通过与ReactiveX运算符词汇对齐的一组丰富的运算符来处理 0..1 (Mono) 和 0..N (Flux) 的数据序列。 Reactor是Reactive Streams库,因此,它的所有运算符都支持无阻塞背压。 Reactor非常注重服务器端Java。它是与Spring紧密合作开发的。

WebFlux需要Reactor作为核心依赖项,但是它可以通过Reactive Streams与其他React库进行互操作。通常,WebFlux API接受普通的发布者作为输入,在内部将其适应于Reactor类型,使用该类型,然后返回 FluxMono 作为输出。因此,您可以将任何发布服务器作为输入传递,并且可以对输出应用操作,但是您需要调整输出以与其他反应式库一起使用。只要可行(例如,带注释的控制器),WebFlux就会透明地适应RxJava或其他反应式库的使用。有关更多详细信息,请参见反应式库。

1.3 对比

2.演示环境
  1. JDK 1.8.0_201
  2. Spring Boot 2.2.0.RELEASE
  3. 构建工具(apache maven 3.6.3)
  4. 开发工具(IntelliJ IDEA )
3.演示代码

3.1 代码说明

通过 @RestController 类似 Spring Mvc 的方式演示 Spring WebFlux 的使用

3.2 代码结构

image-20200727203818388

3.3 maven 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
</dependencies>

3.4 配置文件

无内容

3.5 java代码

UserModel.java

public class UserModel {
    private Long id;
    private String name;
    private Integer age;
    private String birthday;
    private String address;
    private String phone;

    public UserModel() {}

    public UserModel(Long id, String name, Integer age, String birthday, String address, String phone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
        this.address = address;
        this.phone = phone;
    }

    // get&set&toString
}

UserRepository.java

@Repository
public class UserRepository {

    // 预置两条数据,所以起始值从2开始
    private static final AtomicLong ID_GENERATOR = new AtomicLong(2);

    // 模拟数据库操作
    private static final Map<Long, UserModel> USER_MAP = new HashMap<>();

    @PostConstruct
    public void init() {
        UserModel user1 = new UserModel(1L, "zhangsan", 20, "2000-01-02", "beijing", "13666666666");
        UserModel user2 = new UserModel(2L, "lisi", 30, "1990-03-23", "shanghai", "13888888888");
        USER_MAP.put(user1.getId(), user1);
        USER_MAP.put(user2.getId(), user2);
    }

    public List<UserModel> findAll() {
        return new ArrayList<>(USER_MAP.values());
    }

    public UserModel findById(Long id) {
        return USER_MAP.get(id);
    }

    public UserModel add(UserModel userModel) {
        long id = ID_GENERATOR.incrementAndGet();
        userModel.setId(id);
        USER_MAP.put(id, userModel);
        return userModel;
    }

    public UserModel update(UserModel userModel) {
        USER_MAP.put(userModel.getId(), userModel);
        return USER_MAP.get(userModel.getId());
    }

    public UserModel deleteById(Long id) {
        UserModel userModel = USER_MAP.get(id);
        USER_MAP.remove(id);
        return userModel;
    }
}

UserController.java

@RestController
@RequestMapping(value = "/user")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping(value = "/list")
    public Flux<UserModel> list() {
        return Flux.fromStream(userRepository.findAll().stream());
    }

    @GetMapping(value = "/find/{id}")
    public Mono<UserModel> findById(@PathVariable Long id) {
        return Mono.just(userRepository.findById(id));
    }

    @PostMapping(value = "/add")
    public Mono<UserModel> add(@RequestBody UserModel userModel) {
        return Mono.just(userRepository.add(userModel));
    }

    @PutMapping(value = "/update")
    public Mono<UserModel> update(@RequestBody UserModel userModel) {
        return Mono.just(userRepository.update(userModel));
    }

    @DeleteMapping(value = "/delete/{id}")
    public Mono<UserModel> deleteById(@PathVariable Long id) {
        return Mono.just(userRepository.deleteById(id));
    }
}

3.6 git 地址

spring-boot/spring-boot-07-webflux/spring-boot-webflux-demo

4.效果展示

启动 SpringBootWebfluxDemoApplication.main 方法,在 spring-boot-webflux-demo.http 访问下列地址,观察输出信息是否符合预期。

查询用户列表

### GET /user/list
GET http://localhost:8080/user/list
Accept: application/json

image-20200727204816422

根据id查询用户

### GET /user/find/{id}
GET http://localhost:8080/user/find/1
Accept: application/json

image-20200727204913432

新增用户

### POST /user/add
POST http://localhost:8080/user/add
Accept: application/json
Content-Type: application/json

{
  "name": "wangwu",
  "age": 25,
  "birthday": "1995-06-23",
  "address": "guangzhou",
  "phone": "13777777777"
}

image-20200727204953806

修改用户

### PUT /user/update
PUT http://localhost:8080/user/update
Accept: application/json
Content-Type: application/json

{
  "id": 2,
  "name": "lisi2",
  "age": 32,
  "birthday": "1988-03-23",
  "address": "shanghai2",
  "phone": "13888888882"
}

image-20200727205119947

根据id删除用户

### DELETE /user/delete/{id}
DELETE http://localhost:8080/user/delete/3
Accept: application/json

image-20200727205246770

5.源码分析

5.1 Spring WebFlux 运行流程

在 Spring Mvc 中处理核心类是 DispatcherServlet,在 Spring WebFlux 中则是 DispatcherHandler。

image-20200727221259596

  1. 用户发送请求至请求处理器 DispatcherHandler。
  2. DispatcherHandler 收到请求调用 handle 方法,判断是否有 handlerMappings ,如果没有直接返回 404;否则执行下一步。
  3. 从 handlerMappings 找到一个处理当前请求的映射器 HandlerMapping,一般使用的是 url 映射器。
  4. 如果没有找到能处理当前请求的映射器,返回404;否则调用 HandlerMapping。
  5. 使用找到的映射器;并从 handlerAdapters 中找到一个适配器;调用适配器的 handle 方法。
  6. 调用完成返回 HandlerResult,里面封装了返回值,映射器,异常等信息。
  7. 将 handlerResult 返回给 DispatcherHandler。
  8. DispatcherHandler 处理返回的 HandleResult。
  9. 从 resultHandlers 中找到一个结果处理器。
  10. 返回这个结果处理器。
  11. 然后调用它的 handleResult 方法,生成响应信息。
  12. 将响应信息返回,比如直接输出的内容、设置到responseBody中的返回体或者渲染页面等。
  13. 将响应信息返回给 DispatcherHandler 。
  14. DispatcherServlet 返回响应给用户。

5.2 Spring WebFlux 运行原理

image-20200727231134648.png

6.参考
  1. Spring-Boot-Features/WebFlux
  2. Spring Framework/WebFlux

免责声明:文章转载自《14.SpringBoot学习(十四)——Spring Boot WebFlux初体验》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇windows下设置GPU加速tensorflow运算(GT1050)CAS单点登录下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

replace into 批量更新

1、.replace into 批量更新     $sql = replace into test_tbl (id,dr) values (1,'2'),(2,'3'),...(x,'y');   test_tbl 为表名 id 对应的主键或者判断的唯一值,最好是主键,这样数据库检索较快 dr 对应这的字段,有多个字段可以在后面加上 2、insert i...

文本单词one-hot编码

单词->字母->向量 神经网络是建立在数学的基础上进行计算的,因此对数字更敏感,不管是什么样的特征数据都需要以向量的形式喂入神经网络,无论是图片、文本、音频、视频都是一样。 one-hot编码,也就是独热编码,是一种常用的编码手段。在多分类识别的时候,喂入神经网络的标签就是独热码,比如手写数字识别一共有10个分类,某张图片标签是6,则独热码为:...

Mybatis的@Options注解

mybatis的@Options注解能够设置缓存时间,能够为对象生成自增的key 第一个使用场景: 有一个表 CREATE TABLE instance ( instance_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键id', infos VARCHAR(2048) NOT NULL D...

SQL必知必会学习笔记

2.5  select SELECT       要返回的列或表达式     是FROM          从中检索数据的表        仅在从表选择数据时使用where        行级过滤                      否group by  分组说明                      仅在按组计算聚集时使用having     ...

git 学习删除某次提交

cmd里的快捷键 ctrl+a : 跳到开头 ctrl+e : 跳到结尾 which vi 查看vi这么命令在哪个目录下 >> /uer/bin/vi git log 太多了看不完, 空格 往下看 q退出 ctrl+f往后走, ctrl+d 往前走 mkdir a && cd - 创建并进入啊目录 vim下 :...

vue根据id删除某一行

methods:{ del(id){ //ES6 //根据id查找元素 findIndex //let index = arr.findIndex(function(ele,index,arr){return ele.id==id}) //函数内如果返回true,就结束遍历并返回当前index; //...