在响应式的WebFlux出现以前,我们可以使用Spring MVC来编写Rest API,但是传统的基于Servlet的Web框架,在本质上都是阻塞和多线程的,每个连接都会使用一个线程,使用的是线程池中拉取的Worker线程,这里涉及到线程状态的切换,会耗时,且请求线程是阻塞的,直到Worker线程处理完为止。
Spring MVC编写Rest API
先回忆一下Spring MVC来编写Rest API的例子,这里使用《Spring实战(第五版)》中的示例代码:
@RestController
@RequestMapping(path="/design",
produces="application/json")
@CrossOrigin(origins="*")
public class DesignTacoController {
private TacoRepository tacoRepo;
@Autowired
EntityLinks entityLinks;
public DesignTacoController(TacoRepository tacoRepo) {
this.tacoRepo = tacoRepo;
@GetMapping("/recent")
public Iterable<Taco> recentTacos() {
PageRequest page = PageRequest.of(
0, 12, Sort.by("createdAt").descending());
return tacoRepo.findAll(page).getContent();
@PostMapping(consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public Taco postTaco(@RequestBody Taco taco) {
return tacoRepo.save(taco);
//end::postTaco[]
@GetMapping("/{id}")
public Taco tacoById(@PathVariable("id") Long id) {
Optional<Taco> optTaco = tacoRepo.findById(id);
if (optTaco.isPresent()) {
return optTaco.get();
return null;
}
关于几个注解的说明:
@CrossOrigin允许来自任何域的客户端消费该API
Spring WebFlux编写响应式Rest API
异步的Web框架能够以更少的线程获得更高的可扩展性,可以减少线程管理的开销。通过使用所谓的
事件轮询(event looping)
机制,这些框架能够用一个线程处理很多请求,这样每次连接的成本很低。
Spring 5引入了一个非阻塞、异步的Web框架,该框架在很大程度上是基于
Reactor
项目的,能够解决Web应用和API中对更好的可扩展性的需求。
在上图中,Spring MVC是建立在Java Servlet API之上,因此需要Servlet容器才(比如Tomcat)能执行。而WebFlux并不会绑定Servlet API,它构建在Reactive HTTP API之上(其实与Servlet API功能相同,只是采取了响应式的方式),因此WebFlux可以运行在任意非阻塞的Web容器中,包括Netty、Tomcat、Jetty等任意Servlet 3.1及以上的容器。
WebFlux的默认嵌入式服务器是Netty而不是Tomcat。Netty是一个异步、事件驱动的服务器,非常适合Spring WebFlux这样的响应式Web框架。
Spring WebFlux的控制器方法要接受和返回响应式类型,如Reactor中的Mono和Flux,而不是领域类型和集合。Spring WebFlux也能处理RxJava类型,如Observable、Single和Completable。
示例代码如下:
@RestController
@RequestMapping(path = "/design", produces = "application/json")
@CrossOrigin(origins = "*")
public class DesignTacoController {
private TacoRepository tacoRepo;
public DesignTacoController(TacoRepository tacoRepo) {
this.tacoRepo = tacoRepo;
@GetMapping("/recent")
public Flux<Taco> recentTacos() {
return tacoRepo.findAll().take(12);
@PostMapping(consumes = "application/json")
@ResponseStatus(HttpStatus.CREATED)
public Mono<Taco> postTaco(@RequestBody Taco taco) {
return tacoRepo.save(taco);
@GetMapping("/{id}")
public Mono<Taco> tacoById(@PathVariable("id") UUID id) {
return tacoRepo.findById(id);
尽管我们从repository得到了Flux<XXX>,但是直接返回后,由框架为我们调用subscribe(),所以在请求到来时,相应的方法会在数据真正从数据库取出之前就能立即返回。
Spring 5中的函数式模型编写响应式API
上面的代码仍然是使用的注解式WebFlux,虽然注解式很流行,但是仍有一些缺点。比如:
-
所有基于注解的编程方式都会存在 注解该做什么 以及 注解如何做 之间的割裂
注解本身定义了该做什么,但是具体如何做则是在框架代码的其他部分定义的,如果要进行自定义或者扩展,编程模型就会变得很麻烦,因为这样做需要修改注解之外的代码。 -
代码调试比较麻烦,因为无法在注解上设置断点。
Spring 5中新的函数式编程模型像是一个库,而不是一个框架,能让我们在不使用注解的情况下,将请求映射到处理器代码中。
使用Spring的函数式编程模型在编写API会涉及4个主要的类型:
-
RequestPredicate :声明要处理的请求类型
-
RouterFunction : 声明如何将请求路由到处理器代码中
-
ServerRequest : 代表一个Http请求,包括对请求头和请求体的访问
-
ServerResponse : 代表一个Http响应,包括响应头和响应体信息
一个代码示例:
@Configuration
public class RouterFunctionConfig {
@Autowired
private TacaoRepository tacoRepo;
@Bean
public RouterFunction<?> routerFunction() {
return route(GET("/design/taco"),this::recents)
.andRoute(POST("/design"),this::postTaco);
public Mono<ServerResponse> recents(ServerRequest request) {
return ServerResponse.ok()
.body(tacoPepo.findAll().take(12),Taco.class);
public Mono<ServerResponse> postTaco(ServerRequest request) {
Mono<Taco> taco = request.bodyToMono(Taco.class);
Mono<Taco> savedTaco = tacoPepo.save(taco);
return ServerResponse.created(URI.created("http://localhost:8080/design/taco/" +
savedTaco.getId()))
.body(savedTaco,Taco.class);
2020/7/7 补充:一个博主写的关于响应式事务和数据库的博文:
https://www.jdon.com/tags/39413