1、WebFlux简介

springWebFlux 是 SpringFrameworl5.0 添加的新功能,WebFlux 本身是追随当下最火的 Reactive Programming 而诞生的框架,那么本编就来简述下这个框架到底是做什么的。

参考:Spring-WebFlux简介与使用

1.1、关于WebFlux

我们知道传统的Web框架,比如说:struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的,在Servlet3.1之后才有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上,因此它的运行环境的可选择行要比传统web框架多的多。

根据官方的说法,webflux主要在如下两方面体现出独有的优势:

  1. 非阻塞式
    其实在servlet3.1提供了非阻塞的API,WebFlux提供了一种比其更完美的解决方案。使用非阻塞的方式可以利用较小的线程或硬件资源来处理并发进而提高其可伸缩性
  2. 函数式编程端点
    老生常谈的编程方式了,Spring5必须让你使用java8,那么函数式编程就是java8重要的特点之一,而WebFlux支持函数式编程来定义路由端点处理请求。

1.2、SpringMVC与SpringWebFlux

我们先来看官网的一张图:
在这里插入图片描述
  它们都可以用注解式编程模型,都可以运行在tomcat,jetty,undertow等servlet容器当中。但是SpringMVC采用命令式编程方式,代码一句一句的执行,这样更有利于理解与调试,而WebFlux则是基于异步响应式编程,对于初次接触的码农们来说会不习惯。对于这两种框架官方给出的建议是:

  1. 如果原先使用用SpringMVC好好的话,则没必要迁移。因为命令式编程是编写、理解和调试代码的最简单方法。因为老项目的类库与代码都是基于阻塞式的。
  2. 如果你的团队打算使用非阻塞式web框架,WebFlux确实是一个可考虑的技术路线,而且它支持类似于SpringMvc的Annotation的方式实现编程模式,也可以在微服务架构中让WebMvc与WebFlux共用Controller,切换使用的成本相当小。
  3. 在SpringMVC项目里如果需要调用远程服务的话,你不妨考虑一下使用WebClient,而且方法的返回值可以考虑使用Reactive Type类型的,当每个调用的延迟时间越长,或者调用之间的相互依赖程度越高,其好处就越大。

在这里插入图片描述
  我个人意见是:官网明确指出,SpringWebFlux并不是让你的程序运行的更快(相对于SpringMVC来说),而是在有限的资源下提高系统的伸缩性,因此当你对响应式编程非常熟练的情况下并将其应用于新的系统中,还是值得考虑的,否则还是老老实实的使用WebMVC吧

1.3、Reactive Spring Web

在这里定义了最基本的服务端接口:HttpHandlerWebHandler

1.3.1、HttpHandler

HttpHandler 定义了最基本的处理Http请求行为,这个接口主要作用是处理Http请求并将结果做出响应,下面这个表格是说明了Server API的使用方式及何种方式进行响应式流支持的:

Server nameServer API usedReactive Streams support
NettyNetty APIReactor Netty
UndertowUndertow APIspring-web: Undertow to Reactive Streams bridge
TomcatServlet 3.1 non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[]spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge
JettyServlet 3.1 non-blocking I/O; Jetty API to write ByteBuffers vs byte[]spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge
Servlet 3.1 containerServlet 3.1 non-blocking I/Ospring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge

1.3.2、WebHandler

WebHandler 定义了Web 请求必要的一些处理行为,大家不妨想想看:WebFlux已经脱离了Servlet API,那么使用WebFlux时遇到会话机制怎么办,想要对请求过滤处理怎么办或者想要处理静态资源怎么办等等,那么WebHandler 就是做这个事情的。其实在HttpHandler的基本实现类通过适配器模式及装饰模式也间接的实现了WebHandler接口:
在这里插入图片描述
WebHandler常见的实现类,我在这里列举一下:

  • WebHandlerDecorator:WebHandler的装饰器,利用装饰模式实现相关功能的扩展
  • HttpWebHandlerAdapter: 进行Http请求处理,同时也是HttpHandler的实现类
  • FilteringWebHandler:通过WebFilter进行过滤处理的类,类似于Servlet中的Filter
  • ExceptionHandlingWebHandler: 针对于异常的处理类
  • ResourceWebHandler:用于静态资源请求的处理类
  • DispatcherHandler:请求的总控制器,类似于WebMVC中的DispatcherServlet

1.4、认识 Flux 和 Mono

1.4.1、Flux 和 Mono 是 Reactor 中的两个基本概念

  1. Flux 表示的是包含 0 到 N 个元素的异步序列。

在该序列中可以包含三种不同类型的消息通知:
正常的包含元素的消息序列结束的消息序列出错的消息
当消息通知产生时,订阅者中对应的方法 onNext(), onComplete()onError() 会被调用

  1. Mono 表示的是包含 0 或者 1 个元素的异步序列。

该序列中同样可以包含与 Flux 相同的三种类型的消息通知。
Flux 和 Mono 之间可以进行转换。
对一个 Flux 序列进行计数操作,得到的结果是一个 Mono 对象。
把两个 Mono 序列合并在一起,得到的是一个 Flux 对象。

2、WebFlux应用

2.1、配置WebFlux依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

2.2、编写控制器(基于注解)

com/example/test2/controller/HelloWorldWebFlux.java

package com.example.test2.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class HelloWorldWebFlux {

    @RequestMapping("helloworld")
    public Mono<String> helloworld() {
        return Mono.just("Hello WebFlux!");
    }
}

2.3、基于SpringMvc注解

与使用SpringMvc不同的是使用SpringWebFlux同一使用Mono<>,Flux<>对象同意返回数据,如下

@RestController
@RequestMapping("/api/user")
public class WebFluxController {

    private Map<Long,User> map = new HashMap<Long,User>(10);
    @PostConstruct
    public void init(){
        map.put(1L,new User(1,"admin","admin"));
        map.put(2L,new User(1,"admin2","admin2"));
        map.put(3L,new User(1,"admin3","admin3"));
    }
    @GetMapping("/getAll")
    public Flux<User> getAllUser(){
        return Flux.fromIterable(map.entrySet().stream().map(Map.Entry::getValue)
                .collect(Collectors.toList()));
    }
    @GetMapping("/{id}")
    public Mono<User> getUserById(@PathVariable("id") Long id){
        return Mono.just(map.get(id));
    }
    @PostMapping("/save")
    public Mono<ResponseEntity<String>> save(@RequestBody User user){
        map.put(user.getUid(),user);
        return Mono.just(new ResponseEntity<>("添加成功", HttpStatus.CREATED));
    }
}

2.4、基于响应式开发

处理请求的类,实现具体的业务逻辑,接口 ServerRequest 表示的是一个 HTTP 请求体。通过ServerRequest 对象可获取到请求的相关信息,如请求路径、查询参数和请求内容等。方法 的返回值是一个 Mono对象。接口 ServerResponse 用来表示 HTTP 响应。ServerResponse 中包含了很多静态方法来创建不同 HTTP 状态码的响应对象

2.4.1、编写处理类Handler

Handler相当于MVC中的Controller

@Component
public class UserHandler {

    private IUserService userService;
    @Autowired
    public UserHandler(IUserService userService) {
        this.userService = userService;
    }

    public Mono<ServerResponse> getAllUser(ServerRequest serverRequest){
        Flux<User> allUser = userService.getAllUser();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allUser,User.class);
    }

    public Mono<ServerResponse> getUserById(ServerRequest serverRequest){
        //获取url上的id
        Long uid = Long.valueOf(serverRequest.pathVariable("id"));
        Mono<User> user = userService.getUserById(uid);
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(user,User.class);
    }

    public Mono<ServerResponse> saveUser(ServerRequest serverRequest){
        Mono<User> user = serverRequest.bodyToMono(User.class);
        return ServerResponse.ok().build(userService.saveUser(user));
    }

}

2.4.2、编写路由类Router

Router的主要功能是提供路由映射,相当于MVC模式中的注解@RequestMapping

@Configuration
public class RoutingConfiguration {

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler){
        return route(GET("/api/user").and(accept(MediaType.APPLICATION_JSON)),userHandler::getAllUser)
          .andRoute(GET("/api/user/{id}").and(accept(MediaType.APPLICATION_JSON)),userHandler::getUserById)
          .andRoute(POST("/api/save").and(accept(MediaType.APPLICATION_JSON)),userHandler::saveUser);
    }

}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐