一、什么是WebFlux?
- WebFlux是异步非阻塞的,SpringMVC是同步阻塞的
- 响应式一般用Netty或者Servlet 3.1的容器(因为支持异步非阻塞),而Servlet技术栈用的是Servlet容器
二、基于Spring MVC注解的方式
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2、案例
- Mono表示的是包含0或者1个元素的异步序列
- Flux表示的是包含0到N个元素的异步序列
@RestController
@RequestMapping("/api")
public class WebfluxDemoController {
@GetMapping("/mono")
public Mono<String> mono() {
return Mono.just("hello world");
}
@GetMapping(value = "/flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> flux() {
return Flux.fromStream(IntStream.range(1, 5).mapToObj(i -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello--" + i;
}));
}
}
应用启动后发现应用运行于Netty上
浏览器访问http://localhost:8080/api/flux,可以看到Flux可以多次返回数据(通过SSE实现)
3、SSE(Server-Sent Events)
严格地说,HTTP协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息
也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载
SSE就是利用这种机制,使用流信息向浏览器推送信息。它基于HTTP协议,目前除了IE/Edge,其他浏览器都支持
4、整合MongoDB
1)、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
2)、主启动类添加@EnableReactiveMongoRepositories
注解
3)、添加mongodb配置信息
spring.data.mongodb.uri=mongodb://localhost:27017/webflux
4)、实现代码
@Document(collection = "user")
@Data
public class User {
@Id
private String id;
private String name;
private Integer age;
}
@Repository
public interface UserRepository extends ReactiveMongoRepository<User, String> {
}
@RestController
@RequestMapping("/api/user")
public class UserController {
private final UserRepository repository;
public UserController(UserRepository repository) {
this.repository = repository;
}
/**
* 以数组形式一次性返回数据
*
* @return
*/
@GetMapping
public Flux<User> getAllUser() {
return repository.findAll();
}
/**
* 以SSE形式多次返回数据
*
* @return
*/
@GetMapping(value = "stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamGetAll() {
return repository.findAll();
}
/**
* 新增用户信息
*
* @param user
* @return
*/
@PostMapping
public Mono<User> createUser(@RequestBody User user) {
//Spring Data Jpa里面,新增和修改都是save,有id是修改,id为空是新增
user.setId(null);
return this.repository.save(user);
}
/**
* 根据ID删除用户信息
*
* @param id
* @return
*/
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Void>> deleteUserById(@PathVariable String id) {
//deleteById没有返回值,不能判断数据是否存在
//this.repository.deleteById(id);
return this.repository.findById(id)
//当需要操作数据,并返回一个Mono 这时候使用flatMap
//如果不操作数据,只是转换数据 使用map
.flatMap(user -> this.repository.delete(user)
.then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
/**
* 修改用户信息
*
* @param user
* @return
*/
@PutMapping("/{id}")
public Mono<ResponseEntity<User>> updateUser(
@PathVariable String id,
@RequestBody User user) {
return this.repository.findById(id)
.flatMap(u -> {
u.setAge(user.getAge());
u.setName(user.getName());
return this.repository.save(u);
})
.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
/**
* 根据ID查找用户信息
*
* @param id
* @return
*/
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> queryById(
@PathVariable String id) {
return this.repository.findById(id)
.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}
三、基于RouterFunction的模式
@Component
public class UserHandler {
private final UserRepository repository;
public UserHandler(UserRepository repository) {
this.repository = repository;
}
/**
* 获取所有用户信息
*
* @param request
* @return
*/
public Mono<ServerResponse> getAllUser(ServerRequest request) {
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
.body(this.repository.findAll(), User.class);
}
/**
* 新增用户信息
*
* @param request
* @return
*/
public Mono<ServerResponse> createUser(ServerRequest request) {
Mono<User> user = request.bodyToMono(User.class);
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
.body(this.repository.saveAll(user), User.class);
}
/**
* 根据ID删除用户信息
*
* @param request
* @return
*/
public Mono<ServerResponse> deleteUserById(ServerRequest request) {
String id = request.pathVariable("id");
return this.repository.findById(id).flatMap(
user -> this.repository.delete(user).then(ServerResponse.ok().build())
.switchIfEmpty(ServerResponse.notFound().build()));
}
}
@Configuration
public class AllRouters {
@Bean
RouterFunction<ServerResponse> userRouter(UserHandler handler) {
return RouterFunctions.nest(
//相当于Controller类上面的@RequestMapping("/api/user")
RequestPredicates.path("/api/user")
//获取所有用户信息
, RouterFunctions.route(RequestPredicates.GET(""), handler::getAllUser)
//新增用户信息
.andRoute(RequestPredicates.POST("").and(RequestPredicates.accept(MediaType.APPLICATION_JSON_UTF8)), handler::createUser)
//根据ID删除用户信息
.andRoute(RequestPredicates.DELETE("/{id}"), handler::deleteUserById));
}
}