WebFlux是什么

Spring WebFlux 是 Spring MVC 的完全非阻塞响应式替代方案,它可以在不增加硬件资源的情况下实现更好的垂直扩展。作为反应式,它现在利用反应式流来允许异步处理从调用返回到服务器的数据。这意味着我们将看到更少的Lists、Collections 甚至单个对象,取而代之的是它们的反应式等价物,例如FluxMono(来自 Reactor)

简单例子

upload successful

上面代码是否看起来很熟悉,它与标准的 Spring MVC 控制器看起来并没有什么不同,但是在阅读这些方法之后,我们可以看到与我们通常期望的返回类型是不一样的。在这个例子中userDao必须是一个反应式存储库或继承于一个反应式存储库,因为我们已经能够直接返回他们的搜索查询的结果,作为参考,反应式存储库将返回一个Flux用于集合和一个Mono用于单一实体。

请求地址路由

在 SpringMVC 中,我们可以通过如下一些注解来控制请求 URL 和处理器之间的映射关系:

  • @RequestMapping
  • @GetMapping
  • @PostMapping
  • @DeleteMapping
  • @PutMapping

这些注解虽然在 WebFlux 中还可以继续使用,不过 WebFlux 也提供了自己的方案:Router。

编写处理器

删除掉UserController编写我们的UserHandler

package com.jarvan.springwebfluxmongodb.config;

import com.jarvan.springwebfluxmongodb.dao.UserDao;
import com.jarvan.springwebfluxmongodb.entity.User;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import static org.springframework.web.reactive.function.server.ServerResponse.ok;

@Component
public class UserHandler {
    final UserDao userDao;

    public UserHandler(UserDao userDao) {
        this.userDao = userDao;
    }

    public Mono<ServerResponse> add(ServerRequest serverRequest) {
        return ok().contentType(MediaType.APPLICATION_JSON)
                .body(userDao.saveAll(serverRequest.bodyToMono(User.class)), User.class);
    }

    public Mono<ServerResponse> delete(ServerRequest serverRequest) {
        return userDao.findById(serverRequest.pathVariable("id"))
                .flatMap(p -> userDao.delete(p).then(ok().build()))
                .switchIfEmpty(ServerResponse.notFound().build());
    }
    public Mono<ServerResponse> getAll(ServerRequest serverRequest) {
        return ok().contentType(MediaType.APPLICATION_JSON)
                .body(userDao.findAll(), User.class);
    }

    public Mono<ServerResponse> findById(ServerRequest serverRequest) {
        return ok().contentType(MediaType.APPLICATION_JSON)
                .body(userDao.findById(serverRequest.pathVariable("id")), User.class);
    }
}

配置路由

接下来我们把请求的 URL 地址和这些处理器之间关联起来,配置如下

package com.jarvan.springwebfluxmongodb.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;

@Configuration
public class UserRouter {

    @Bean
    public RouterFunction<ServerResponse> route(UserHandler userHandler) {
        return RouterFunctions.nest(RequestPredicates.path("/user"),
                RouterFunctions.route(RequestPredicates.POST("/add"), userHandler::add)
                        .andRoute(RequestPredicates.DELETE("/delete/{id}"), userHandler::delete)
                        .andRoute(RequestPredicates.GET("/getAll").and(accept(APPLICATION_JSON)), userHandler::getAll)
                        .andRoute(RequestPredicates.GET("/{id}").and(accept(APPLICATION_JSON)), userHandler::findById)
        );

    }
}

上面这个配置类

1、首先调用nest()方法,第一个参数配置的相当于是接下来配置的地址的一个前缀,这有点类似于我们在Controller类上直接写@RequestMapping` 注解去配置地址。

2、nest() 方法的第二个参数就是 RouterFunction 实例了,每一个 RouterFunction 实例通过 RouterFunctions.route 方法来构建,它的第一个参数就是请求的 URL 地址(注意这个时候配置的地址都是有一个共同的前缀),第二个参数我们通过方法引用的方式配置了一个 HandlerFunction,这个就是当前请求的处理器了。

3、通过.andRoute可以配置多个路由。

此处注意路由匹配的优先级,如下图,如果先匹配/{id},则/user/getAll方法将会匹配/{id},然后去查id为getAll的数据

upload successful

测试

add

upload successful

查询

upload successful

删除

如果删除的数据存在响应200、不存在将返回404

upload successful

upload successful