Spring WebFlux 入门

1,124 阅读3分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战


WebFlux是Spring Framework 5.0 发布的基于Reactor异步、非阻塞的web框架,使用WebFlux可以充分的利用计算的多核优势提升系统的吞吐量和伸缩性。

WebFlux并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性

下图展示了Spring WebFluxSpring MVC的应用对比

image.png

  • 运行容器 Spring WebFlux: 可以运行在Netty, UndertowServlet 3.1以上的容器中 Spring MVC: 可以运行在Servlet 3.1容器中
  • 容器与应用交互API Spring WebFlux: 使用Reactor Stream Adapters Spring MVC: 使用Servlet API
  • 安全 Spring WebFlux: 使用Spring Security Reactor安全框架 Spring MVC: 使用Spring Security等同步安全框架
  • 数据存储 Spring WebFlux:使用Spring Data Reactor Repositories和数据库交互,支持(Mongo、Cassandra、Redis、CouchBase、R2DBC) Spring MVC: 使用Spring Data Repositories和数据库交互

WebFlux提供了两种使用方式:注解式(Annotated Controllers)和 函数式(Functional Endpoints

  • 注解式:和SpringMvc的注解一致,使用RestControllerGetMappingPostMapping等注解
  • 函数式:基于Lambda表达式,使用Function描述请求端点

注解式(Annotated Controllers

SpringMvc的注解一致,使用RestControllerGetMappingPostMapping等注解,支持Spring validation参数效验

函数式(Functional Endpoints

WebFlux的函数式使用RouterHandler处理请求。RouterFunction负责接收HTTP请求,并找到对应的HandlerFunctionHandlerFunction处理HTTP请求,并返回一个延迟的ServerResponse

HandlerFunction

HandlerFunction使用ServerRequestServerResponse处理请求和响应

ServerRequest

ServerRequest提供了访问HTTP请求的method, URI, headersquery parameters

         // 获取请求的method
         HttpMethod method = serverRequest.method();

         // 获取请求的URI
         URI uri = serverRequest.uri();

         // 获取请求的headers
         ServerRequest.Headers headers = serverRequest.headers();

         // 获取指定key的查询参数
         Optional<String> id = serverRequest.queryParam("id");
         // 获取请求全部的查询参数
         MultiValueMap<String, String> stringStringMultiValueMap = serverRequest.queryParams();

         // 获取请求路径
         RequestPath requestPath = serverRequest.requestPath();
提取request body并转成Mono或Flux
 // 获取request body并转成Mono<String>
 Mono<String> string = serverRequest.bodyToMono(String.class);
 //  获取request body并转成Flux<User>
 Flux<Person> people = serverRequest.bodyToFlux(User.class);
 Mono<String> string = serverRequest.body(BodyExtractors.toMono(String.class));
 Flux<Person> people = serverRequest.body(BodyExtractors.toFlux(User.class));
 
 //  获取formData数据
 Mono<MultiValueMap<String, String>> map = serverRequest.formData();
 //  获取multipartData数据
 Mono<MultiValueMap<String, Part>> map1 = serverRequest.multipartData();
 //  获取request body数据,并转成Flux<Part>
 Flux<Part> parts = serverRequest.body(BodyExtractors.toParts());

ServerResponse

ServerResponse可以访问HTTP请求的Response, 可以使用builder模式设置响应状态、响应头和响应体

 User user = User.builder()
         .id(1)
         .name("张三")
         .age(18)
         .build();
 
 return ServerResponse.ok().body(user, User.class);

使用Lambda表达式构造HandlerFunction

 HandlerFunction<ServerResponse> helloWorld = request -> ServerResponse.ok().bodyValue("Hello World");

RouterFunction

RouterFunction负责为请求找到对应的HandlerFunction进行处理

 RouterFunction<ServerResponse> route = RouterFunctions.route()
     .GET("/person/{id}",, handler::getPerson)
     .POST("/person", handler::createPerson)
     .build();

RequestPredicate(请求断言)

GETPOST等方法都有用于接受请求断言参数的重载方法。请求方法和路径匹配后,RequestPredicate返回true的请求才会被对应的HandlerFunction处理

image.png

RouterFunction<ServerResponse> route = route()
         // accept限制了该请求的类型是JSON格式
        .GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
        .POST("/person", handler::createPerson)
        .build();

Nested Routes(嵌套路由)

使用path方法可以将多个请求的相同前缀提取出来

 RouterFunction<ServerResponse> route = route()
     .path("/person", builder -> builder 
         .GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
         .POST("/person", handler::createPerson))
     .build();

参数验证

可以使用Spring validation进行参数验证

  1. 添加依赖

             <dependency>
                 <groupId>org.hibernate.validator</groupId>
                 <artifactId>hibernate-validator</artifactId>
                 <version>6.2.0.Final</version>
             </dependency>
    
  2. 编写实体的验证器

     import org.springframework.validation.Validator;
     
     public class UserValidator implements Validator {
     
         @Override
         public boolean supports(Class<?> aClass) {
             return User.class.equals(aClass);
         }
     
         @Override
         public void validate(Object o, Errors errors) {
     
             ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
             User user = (User) o;
             if (user.getAge() < 20) {
                 errors.rejectValue("age", "negativevalue");
             } else if (user.getAge() > 110) {
                 errors.rejectValue("age", "too.darn.old");
             }
         }
     }
    
  3. 应用参数验证

     public Mono<ServerResponse> addUser(ServerRequest serverRequest){
     
         // 在doOnNext方法中绑定验证方法
         serverRequest.bodyToMono(User.class) .doOnNext(this::validate);
     
         return ServerResponse.ok().body("122", String.class);
     }
     
     // 定义验证方法
     private void validate(User user) {
         // 实例化实体验证器
         Validator validator = new UserValidator();
         Errors errors = new BeanPropertyBindingResult(user, "user");
         validator.validate(user, errors);
         if (errors.hasErrors()) {
             throw new ServerWebInputException(errors.toString());
         }
     }
    

Handlers 也可以通过LocalValidatorFactoryBean注入一个全局的Validator,使用standard bean validation API(JSR-303)验证