一、什麼是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));
}
}