14.SpringBoot学习(十四)——Spring Boot WebFlux初体验
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
andFlux
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 aFlux
or aMono
as output. So, you can pass anyPublisher
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类型,使用该类型,然后返回 Flux
或Mono
作为输出。因此,您可以将任何发布服务器作为输入传递,并且可以对输出应用操作,但是您需要调整输出以与其他反应式库一起使用。只要可行(例如,带注释的控制器),WebFlux就会透明地适应RxJava或其他反应式库的使用。有关更多详细信息,请参见反应式库。
1.3 对比
2.演示环境
- JDK 1.8.0_201
- Spring Boot 2.2.0.RELEASE
- 构建工具(apache maven 3.6.3)
- 开发工具(IntelliJ IDEA )
3.演示代码
3.1 代码说明
通过 @RestController
类似 Spring Mvc 的方式演示 Spring WebFlux 的使用
3.2 代码结构
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
根据id查询用户
### GET /user/find/{id}
GET http://localhost:8080/user/find/1
Accept: application/json
新增用户
### 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"
}
修改用户
### 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"
}
根据id删除用户
### DELETE /user/delete/{id}
DELETE http://localhost:8080/user/delete/3
Accept: application/json
5.源码分析
5.1 Spring WebFlux 运行流程
在 Spring Mvc 中处理核心类是 DispatcherServlet,在 Spring WebFlux 中则是 DispatcherHandler。
- 用户发送请求至请求处理器 DispatcherHandler。
- DispatcherHandler 收到请求调用 handle 方法,判断是否有 handlerMappings ,如果没有直接返回 404;否则执行下一步。
- 从 handlerMappings 找到一个处理当前请求的映射器 HandlerMapping,一般使用的是 url 映射器。
- 如果没有找到能处理当前请求的映射器,返回404;否则调用 HandlerMapping。
- 使用找到的映射器;并从 handlerAdapters 中找到一个适配器;调用适配器的 handle 方法。
- 调用完成返回 HandlerResult,里面封装了返回值,映射器,异常等信息。
- 将 handlerResult 返回给 DispatcherHandler。
- DispatcherHandler 处理返回的 HandleResult。
- 从 resultHandlers 中找到一个结果处理器。
- 返回这个结果处理器。
- 然后调用它的 handleResult 方法,生成响应信息。
- 将响应信息返回,比如直接输出的内容、设置到responseBody中的返回体或者渲染页面等。
- 将响应信息返回给 DispatcherHandler 。
- DispatcherServlet 返回响应给用户。