WebFlux入门

WebFlux

WebFlux跟Spring MVC齐名,是在Spring5时引入的。Spring MVC是基于Servlet API和Servlet容器设计的,而WebFlux是基于Reactive Stream API和Servlet 3.1+ 容器设计

Reactive Stream

Reactive Stream是一种响应式流编程规范,目前流行的实现该规范的有RxJava和Reactor,WebFlux使用的是Reactor。响应式编程是利用发布订阅模式,生产者生产,消费者消费元素。常见的是pull/push模式。

WebFlux和SpringMVC对比

WebFlux 有什么优势呢?Spring官网是这么说的:

1
2
3
4
5
Servlet 3.1 did provide an API for non-blocking I/O. However, using it leads away from 
the rest of the Servlet API, where contracts are synchronous (Filter, Servlet) or blocking
(getParameter, getPart). This was the motivation for a new common API to serve as a foundation
across any non-blocking runtime. That is important because of servers (such as Netty) that
are well-established in the async, non-blocking space.The other part of the answer is functional programming.

简单地说WebFlux实现了非阻塞IO,传统的Servlet容器会为每个请求创建一个IO线程,但是线程的数量有限,当并发量大而且每个请求都很耗时的时候,服务器能处理的请求就很少

如果Servlet容器能快速的接受每个请求,将耗时的操作丢给其他线程,这样服务器就能接受更多的请求,所以Non-Blocking IO是指不阻塞接受请求的Servlet线程

一张图说明

还有就是WebFlux中会有大量的Mono和Flux操作,这两个类是Reactor框架操作流的两个重要类,Mono是对单个对象实例的操作,Flux是可以对一个或者多个对象实例的操作

WebFlux不在局限于Servlet容器,除了Tomcat、Jetty等Servlet外,还可支持Netty

快速上手

接下来使用Kotlin演示怎么快速上手WebFlux,WebFlux有两种编程风格,一种是注解式,一种是函数式

注解式

这种形式跟我们使用SpringMVC一样,加上@RequestMapping代表路由,加上@Controller代表控制器等等,反正SpringMVC能支持的注解编程风格,WebFlux也能支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseBody
import reactor.core.publisher.Mono

/**
* @author linnanwei
* @version 1.0.0
* @since 1.0.0
* Date: 2019/12/20
*/
@Controller
class HelloController {

@ResponseBody
@GetMapping("hello")
fun hello(name: String?): Mono<String> {
return Mono.just("Hello $name")
}

}

所以,我们可以很好的从SpringMVC过渡到WebFlux,有区别的是SpringMVC使用HttpServletRequest和HttpServletResponse,但是WebFlux的实现不再是Servlet

函数式

想玩就玩点不一样的。WebFlux支持函数式编程,里面有大量Lambda表达式,而如果使用Kotlin来实现,代码会更加简洁,配合Kotlin的DSL表达式,用起来很爽

路由

函数中的路由相当于我们加上注解@RequestMapping的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import com.helper.web.handler.HelloHandler
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType.*
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.router

/**
* @author linnanwei
* @version 1.0.0
* @since 1.0.0
* Date: 2019/12/11
*/
@Configuration
open class RouterConfig(
private val helloHandler: HelloHandler
) {
@Bean
open fun route() = router {
// 声明接受协议
accept(APPLICATION_JSON).nest {
// 注册路由
GET("/hello", helloHandler::hello)
}
}
}

如上,代表get请求的/hello会交给HelloHandler的hello()函数处理,注意,这里的hello函数需要接受一个上下文参数ServerRequest

Handler

Handler你也可以理解为Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.BodyInserters.fromObject
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

/**
* @author linnanwei
* @version 1.0.0
* @since 1.0.0
* Date: 2019/12/20
*/
@Component
class HelloHandler {

fun hello(request: ServerRequest): Mono<ServerResponse> {
// 寻找参数name
val name = request.queryParam("name").get()
return ServerResponse.ok().body(fromObject("Hello, $name"))
}

}
1
2
F:\Code\homework-helper-parent>curl http://localhost:8080/hello?name=WebFlux
Hello, WebFlux

添加Filter

在SpringMVC中拦截器或者过滤器,但在WebFlux只有Filter,提供请求前后的拦截功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

import com.helper.common.domain.ServiceResult
import org.springframework.web.reactive.function.BodyInserters.fromObject
import org.springframework.web.reactive.function.server.HandlerFilterFunction
import org.springframework.web.reactive.function.server.HandlerFunction
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

/**
* 拦截器:session检测拦截
* @author linnanwei
* @version 1.0.0
* @since 1.0.0
* Date: 2019/12/11
*/
class SessionWebFilter : HandlerFilterFunction<ServerResponse, ServerResponse> {

companion object {
val STU_URL = "/stu"
val ADMIN_URL = "/admin"
val TEACH_URL = "/teacher"
}

override fun filter(request: ServerRequest, next: HandlerFunction<ServerResponse>): Mono<ServerResponse> {
val session = request.session().block()
val path = request.path()
when {
path.contains(STU_URL) -> session.getAttribute<String>("stuId") ?: return ServerResponse.status(403)
.body(fromObject(ServiceResult.fail<Any>(4011, "You have to Login!")))
path.contains(ADMIN_URL) -> session.getAttribute<String>("stuId") ?: return ServerResponse.status(403)
.body(fromObject(ServiceResult.fail<Any>(4013, "You have to Login!")))
path.contains(TEACH_URL) -> session.getAttribute<String>("stuId") ?: return ServerResponse.status(403)
.body(fromObject(ServiceResult.fail<Any>(4012, "You have to Login!")))
}
return next.handle(request)
}

}

该过滤器的功能很简单,判断session存不存在,不存在返回提示信息

接下来只需要将这个Filter注册到上面的路由信息中即可

1
2
3
4
5
6
7
8
@Bean
open fun route() = router {
// 声明接受协议
accept(APPLICATION_JSON).nest {
// 注册路由
GET("/hello", helloHandler::hello)
}
}.filter(MyWebFilter()) // 注册Filter拦截器

资料

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html

https://www.cnblogs.com/zhoutao825638/p/10382275.html