一、简介
Spring WebFlux
Spring 官方文档对 Spring WebFlux 介绍如下:
Spring Framework 5 includes a new spring-webflux module. The module contains support for reactive HTTP and WebSocket clients as well as for reactive server web applications including REST, HTML browser, and WebSocket style interactions.
Spring Framework 5 提供了一个新的
spring-webflux
模块。该模块包含了:
- 对响应式支持的 HTTP 和 WebSocket 客户端。
- 对响应式支持的 Web 服务器,包括 Rest API、HTML 浏览器、WebSocket 等交互方式。
On the server-side WebFlux supports 2 distinct programming models:
- Annotation-based with @Controller and the other > annotations supported also with Spring MVC
- Functional, Java 8 lambda style routing and handling
下图显示了服务端的技术栈,左侧是
spring-webmvc
模块中传统的、基于 Servlet 的 Spring MVC ,右侧是spring-webflux
模块中的响应式技术栈。
- 仔细看第一层的两个框框,分别是上面提到的 WebFlux 的两种编程模型。表达的是 SpringMVC 不支持 Router Functions 方式,而 WebFlux 支持。
WebFlux can run on Servlet containers with support for the Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as Netty and Undertow.
WebFlux 可以运行在:
- 支持 Servlet 3.1 非阻塞 IO API 的 Servlet 容器上
- 也可以运行在支持异步运行时的,例如说 Netty 或者 Undertow 上
Each runtime is adapted to a reactive ServerHttpRequest and ServerHttpResponse exposing the body of the request and response as Flux, rather than InputStream and OutputStream, with reactive backpressure.
每一个运行时(runtime)适用于将响应式的 ServerHttpRequest 和 ServerHttpResponse 中 request 和 response 的 body 暴露成
Flux<DataBuffer>
对象,而不是 InputStream 和 InputStream 对象,可用于响应式中的背压(backpressure)。这段有点晦涩,简单来说:
- 对于 Servlet 来说,
ServletRequest#getInputStream()
方法,获得请求的主体内容返回的是 InputStream 对象。- 对于 WebFlux 来说,
ServerHttpRequest#getBody()
方法,获得请求的主体内容返回的是Flux<DataBuffer>
对象。REST-style JSON and XML serialization and deserialization is supported on top as a
Flux<Object>
, and so is HTML view rendering and Server-Sent Events.REST 风格 API 使用到的 JSON 和 XML 序列化和反序列化,需要提供对
Flux<Object>
的支持。对于 HTML 渲染,和 SSE 也要提供对Flux<Object>
的支持。
二、快速入门
1、引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-boot-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.1.RELEASE</version>
</parent>
<groupId>com.mscloudmesh</groupId>
<artifactId>springboot-webflux</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.实体类UserInfo
package com.mscloudmesh.webflux.annotation.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Data
@Slf4j
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo implements java.io.Serializable {
private Long id;
private String userName;
private Integer age;
}
3、数据交互层UserRepository(这里用一个map模拟数据库操作)
package com.mscloudmesh.webflux.annotation.repository;
import com.mscloudmesh.webflux.annotation.model.UserInfo;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
@Repository
public class UserRepository {
private ConcurrentMap<Long, UserInfo>
userDataMap = new ConcurrentHashMap<Long, UserInfo>();
private static final AtomicLong idGenerator = new AtomicLong(0);
public UserInfo save(UserInfo userInfo) {
Long id = idGenerator.incrementAndGet();
UserInfo info = new UserInfo(id, "kevin", 30);
userDataMap.put(id, userInfo);
return userInfo;
}
public Collection<UserInfo> findAll() {
return userDataMap.values();
}
public UserInfo findByUsrId(Long id) {
return userDataMap.get(id);
}
public UserInfo updateUserInfo(UserInfo userInfo) {
userDataMap.put(userInfo.getId(), userInfo);
return userInfo;
}
public Long deleteUserById(Long id) {
userDataMap.remove(id);
return id;
}
}
4.handler实现
package com.mscloudmesh.webflux.annotation.service;
import com.mscloudmesh.webflux.annotation.model.UserInfo;
import com.mscloudmesh.webflux.annotation.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class UserHandler {
@Autowired
private UserRepository repository;
public Mono<UserInfo> save(UserInfo userInfo) {
return Mono.create(userInfoMonoSink -> userInfoMonoSink.success(repository.save(userInfo)));
}
public Mono<UserInfo> findByUserId(Long id) {
return Mono.justOrEmpty(repository.findByUsrId(id));
}
public Mono<UserInfo> updateUser(UserInfo userInfo) {
return Mono.create(userInfoMonoSink -> userInfoMonoSink.success(repository.save(userInfo)));
}
public Mono<Long> deleteUserById(Long id) {
return Mono.create(userInfoMongoSink -> userInfoMongoSink.success(repository.deleteUserById(id)));
}
}
5.controller测试类
package com.mscloudmesh.webflux.annotation.controller;
import com.mscloudmesh.webflux.annotation.model.UserInfo;
import com.mscloudmesh.webflux.annotation.service.UserHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/webflux")
public class UserWebfluxController {
@Autowired
private UserHandler userHandler;
@RequestMapping("/save")
public Mono<UserInfo> save(UserInfo info){
return userHandler.save(info);
}
@RequestMapping("/findById")
public Mono<UserInfo> findById(Long id ){
return userHandler.findByUserId(id);
}
}
开始测试一下: