Spring WebFlux 入门
1. WebFlux介绍
Spring WebFlux 是 Spring Framework 5.0中引入的新的响应式web框架。与Spring MVC不同,它不需要Servlet API,是完全异步且非阻塞的,并且通过Reactor项目实现了Reactive Streams规范。
Spring WebFlux 用于创建基于事件循环执行模型的完全异步且非阻塞的应用程序。
(PS:所谓异步非阻塞是针对服务端而言的,是说服务端可以充分利用CPU资源去做更多事情,这与客户端无关,客户端该怎么请求还是怎么请求。)
Reactive Streams是一套用于构建高吞吐量、低延迟应用的规范。而Reactor项目是基于这套规范的实现,它是一个完全非阻塞的基础,且支持背压。Spring WebFlux基于Reactor实现了完全异步非阻塞的一套web框架,是一套响应式堆栈。
【spring-webmvc + Servlet + Tomcat】响应式的、异步非阻塞的
【spring-webflux + Reactor + Netty】命令式的、同步阻塞的
2. Spring WebFlux Framework
Spring WebFlux有两种风格:功能性和基于注释的。基于注释的与Spring MVC非常相近。例如:
1 @RestController
2 @RequestMapping("/users")
3 public class MyRestController {
4
5 @GetMapping("/{user}")
6 public Mono<User> getUser(@PathVariable Long user) {
7 // ...
8 }
9
10 @GetMapping("/{user}/customers")
11 public Flux<Customer> getUserCustomers(@PathVariable Long user) {
12 // ...
13 }
14
15 @DeleteMapping("/{user}")
16 public Mono<User> deleteUser(@PathVariable Long user) {
17 // ...
18 }
19 }
与之等价,也可以这样写:
1 @Configuration
2 public class RoutingConfiguration {
3 @Bean
4 public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
5 return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
6 .andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
7 .andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
8 }
9 }
10
11 @Component
12 public class UserHandler {
13 public Mono<ServerResponse> getUser(ServerRequest request) {
14 // ...
15 }
16 public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
17 // ...
18 }
19 public Mono<ServerResponse> deleteUser(ServerRequest request) {
20 // ...
21 }
22 }
如果你同时添加了spring-boot-starter-web和spring-boot-starter-webflux依赖,那么Spring Boot会自动配置Spring MVC,而不是WebFlux。你当然可以强制指定应用类型,通过SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)
3. Hello WebFlux
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.springframework.boot</groupId>
7 <artifactId>spring-boot-starter-parent</artifactId>
8 <version>2.2.5.RELEASE</version>
9 <relativePath/> <!-- lookup parent from repository -->
10 </parent>
11 <groupId>com.cjs.example</groupId>
12 <artifactId>cjs-reactive-rest-service</artifactId>
13 <version>0.0.1-SNAPSHOT</version>
14 <name>cjs-reactive-rest-service</name>
15
16 <properties>
17 <java.version>1.8</java.version>
18 </properties>
19
20 <dependencies>
21 <dependency>
22 <groupId>org.springframework.boot</groupId>
23 <artifactId>spring-boot-starter-webflux</artifactId>
24 </dependency>
25
26 <dependency>
27 <groupId>org.springframework.boot</groupId>
28 <artifactId>spring-boot-starter-test</artifactId>
29 <scope>test</scope>
30 <exclusions>
31 <exclusion>
32 <groupId>org.junit.vintage</groupId>
33 <artifactId>junit-vintage-engine</artifactId>
34 </exclusion>
35 </exclusions>
36 </dependency>
37 <dependency>
38 <groupId>io.projectreactor</groupId>
39 <artifactId>reactor-test</artifactId>
40 <scope>test</scope>
41 </dependency>
42 </dependencies>
43
44 <build>
45 <plugins>
46 <plugin>
47 <groupId>org.springframework.boot</groupId>
48 <artifactId>spring-boot-maven-plugin</artifactId>
49 </plugin>
50 </plugins>
51 </build>
52
53 </project>
GreetingHandler.java
1 package com.cjs.example.restservice.hello;
2
3 import org.springframework.http.MediaType;
4 import org.springframework.stereotype.Component;
5 import org.springframework.web.reactive.function.BodyInserters;
6 import org.springframework.web.reactive.function.server.ServerRequest;
7 import org.springframework.web.reactive.function.server.ServerResponse;
8 import reactor.core.publisher.Mono;
9
10 import java.util.concurrent.atomic.AtomicLong;
11
12 /**
13 * @author ChengJianSheng
14 * @date 2020-03-25
15 */
16 @Component
17 public class GreetingHandler {
18
19 private final AtomicLong counter = new AtomicLong();
20
21 /**
22 * A handler to handle the request and create a response
23 */
24 public Mono<ServerResponse> hello(ServerRequest request) {
25 return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
26 .body(BodyInserters.fromValue("Hello, Spring!"));
27
28 }
29 }
GreetingRouter.java
1 package com.cjs.example.restservice.hello;
2
3 import org.springframework.context.annotation.Bean;
4 import org.springframework.context.annotation.Configuration;
5 import org.springframework.http.MediaType;
6 import org.springframework.web.reactive.function.server.*;
7
8 /**
9 * @author ChengJianSheng
10 * @date 2020-03-25
11 */
12 @Configuration
13 public class GreetingRouter {
14
15 /**
16 * The router listens for traffic on the /hello path and returns the value provided by our reactive handler class.
17 */
18 @Bean
19 public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
20 return RouterFunctions.route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), greetingHandler::hello);
21 }
22 }
GreetingWebClient.java
1 package com.cjs.example.restservice.hello;
2
3 import org.springframework.http.MediaType;
4 import org.springframework.web.reactive.function.client.ClientResponse;
5 import org.springframework.web.reactive.function.client.WebClient;
6 import reactor.core.publisher.Mono;
7
8 /**
9 * @author ChengJianSheng
10 * @date 2020-03-25
11 */
12 public class GreetingWebClient {
13
14 /**
15 * For reactive applications, Spring offers the WebClient class, which is non-blocking.
16 *
17 * WebClient can be used to communicate with non-reactive, blocking services, too.
18 */
19 private WebClient client = WebClient.create("http://localhost:8080");
20
21 private Mono<ClientResponse> result = client.get()
22 .uri("/hello")
23 .accept(MediaType.TEXT_PLAIN)
24 .exchange();
25
26 public String getResult() {
27 return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
28 }
29 }
Application.java
1 package com.cjs.example.restservice;
2
3 import com.cjs.example.restservice.hello.GreetingWebClient;
4 import org.springframework.boot.SpringApplication;
5 import org.springframework.boot.autoconfigure.SpringBootApplication;
6
7 /**
8 * @author ChengJianSheng
9 * @date 2020-03-25
10 */
11 @SpringBootApplication
12 public class CjsReactiveRestServiceApplication {
13
14 public static void main(String[] args) {
15 SpringApplication.run(CjsReactiveRestServiceApplication.class, args);
16
17 GreetingWebClient gwc = new GreetingWebClient();
18 System.out.println(gwc.getResult());
19 }
20
21 }
可以直接在浏览器中访问 http://localhost:8080/hello
GreetingRouterTest.java
1 package com.cjs.example.restservice;
2
3 import org.junit.jupiter.api.Test;
4 import org.junit.jupiter.api.extension.ExtendWith;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.boot.test.context.SpringBootTest;
7 import org.springframework.http.MediaType;
8 import org.springframework.test.context.junit.jupiter.SpringExtension;
9 import org.springframework.test.web.reactive.server.WebTestClient;
10
11 @ExtendWith(SpringExtension.class)
12 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
13 public class GreetingRouterTest {
14
15 @Autowired
16 private WebTestClient webTestClient;
17
18 /**
19 * Create a GET request to test an endpoint
20 */
21 @Test
22 public void testHello() {
23 webTestClient.get()
24 .uri("/hello")
25 .accept(MediaType.TEXT_PLAIN)
26 .exchange()
27 .expectStatus().isOk()
28 .expectBody(String.class).isEqualTo("Hello, Spring!");
29 }
30
31 }
4. Reactor 核心特性
Mono: implements Publisher and returns 0 or 1 elements
Flux: implements Publisher and returns N elements
5. Spring Data Redis
pom.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.springframework.boot</groupId>
7 <artifactId>spring-boot-starter-parent</artifactId>
8 <version>2.2.6.RELEASE</version>
9 <relativePath/> <!-- lookup parent from repository -->
10 </parent>
11 <groupId>com.cjs.example</groupId>
12 <artifactId>cjs-webflux-hello</artifactId>
13 <version>0.0.1-SNAPSHOT</version>
14 <name>cjs-webflux-hello</name>
15
16 <properties>
17 <java.version>1.8</java.version>
18 </properties>
19
20 <dependencies>
21 <dependency>
22 <groupId>org.springframework.boot</groupId>
23 <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
24 </dependency>
25 <dependency>
26 <groupId>org.springframework.boot</groupId>
27 <artifactId>spring-boot-starter-webflux</artifactId>
28 </dependency>
29
30 <dependency>
31 <groupId>org.apache.commons</groupId>
32 <artifactId>commons-pool2</artifactId>
33 <version>2.8.0</version>
34 </dependency>
35 <dependency>
36 <groupId>com.alibaba</groupId>
37 <artifactId>fastjson</artifactId>
38 <version>1.2.67</version>
39 </dependency>
40 <dependency>
41 <groupId>org.projectlombok</groupId>
42 <artifactId>lombok</artifactId>
43 <optional>true</optional>
44 </dependency>
45 <dependency>
46 <groupId>org.springframework.boot</groupId>
47 <artifactId>spring-boot-starter-test</artifactId>
48 <scope>test</scope>
49 <exclusions>
50 <exclusion>
51 <groupId>org.junit.vintage</groupId>
52 <artifactId>junit-vintage-engine</artifactId>
53 </exclusion>
54 </exclusions>
55 </dependency>
56 <dependency>
57 <groupId>io.projectreactor</groupId>
58 <artifactId>reactor-test</artifactId>
59 <scope>test</scope>
60 </dependency>
61 </dependencies>
62
63 <build>
64 <plugins>
65 <plugin>
66 <groupId>org.springframework.boot</groupId>
67 <artifactId>spring-boot-maven-plugin</artifactId>
68 </plugin>
69 </plugins>
70 </build>
71
72 </project>
UserController.java
1 package com.cjs.example.webflux.controller;
2
3 import com.alibaba.fastjson.JSON;
4 import com.cjs.example.webflux.domain.User;
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.data.redis.core.ReactiveHashOperations;
7 import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
8 import org.springframework.web.bind.annotation.*;
9 import reactor.core.publisher.Mono;
10
11 /**
12 * @author ChengJianSheng
13 * @date 2020-03-27
14 */
15 @RestController
16 @RequestMapping("/users")
17 public class UserController {
18
19
20 @Autowired
21 private ReactiveStringRedisTemplate reactiveStringRedisTemplate;
22
23 @GetMapping("/hello")
24 public Mono<String> hello() {
25 return Mono.just("Hello, Reactive");
26 }
27
28 @PostMapping("/save")
29 public Mono<Boolean> saveUser(@RequestBody User user) {
30 ReactiveHashOperations hashOperations = reactiveStringRedisTemplate.opsForHash();
31 return hashOperations.put("USER_HS", String.valueOf(user.getId()), JSON.toJSONString(user));
32 }
33
34 @GetMapping("/info/{id}")
35 public Mono<User> info(@PathVariable Integer id) {
36 ReactiveHashOperations reactiveHashOperations = reactiveStringRedisTemplate.opsForHash();
37 Mono<String> hval = reactiveHashOperations.get("USER_HS", String.valueOf(id));
38 return hval.map(e->JSON.parseObject(e, User.class));
39 }
40
41 }
CoffeeController.java
1 package com.cjs.example.webflux.controller;
2
3 import com.cjs.example.webflux.domain.Coffee;
4 import org.springframework.data.redis.core.*;
5 import org.springframework.web.bind.annotation.GetMapping;
6 import org.springframework.web.bind.annotation.PathVariable;
7 import org.springframework.web.bind.annotation.RequestMapping;
8 import org.springframework.web.bind.annotation.RestController;
9 import reactor.core.publisher.Flux;
10 import reactor.core.publisher.Mono;
11
12 /**
13 * Spring WebFlux is the new reactive web framework introduced in Spring Framework 5.0.
14 * Unlike Spring MVC, it does not require the Servlet API, is fully asynchronous and non-blocking,
15 * and implements the Reactive Streams specification through the Reactor project.
16 *
17 * @author ChengJianSheng
18 * @date 2020-03-27
19 */
20 @RestController
21 @RequestMapping("/coffees")
22 public class CoffeeController {
23
24 private final ReactiveRedisOperations<String, Coffee> coffeeOps;
25
26 public CoffeeController(ReactiveRedisOperations<String, Coffee> coffeeOps) {
27 this.coffeeOps = coffeeOps;
28 }
29
30 @GetMapping("/getAll")
31 public Flux<Coffee> getAll() {
32 return coffeeOps.keys("*").flatMap(coffeeOps.opsForValue()::get);
33 }
34
35 @GetMapping("/info/{id}")
36 public Mono<Coffee> info(@PathVariable String id) {
37 ReactiveValueOperations valueOperations = coffeeOps.opsForValue();
38 return valueOperations.get(id);
39 }
40 }
最后,也是非常重要的一点:异步非阻塞并不会使程序运行得更快。WebFlux 并不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。
Spring WebFlux 是一个异步非阻塞的 Web 框架,所以,它特别适合应用在 IO 密集型的服务中,比如微服务网关这样的应用中。
Reactive and non-blocking generally do not make applications run faster.
6. Docs
https://projectreactor.io/docs/core/release/reference/index.html
https://projectreactor.io/docs/core/release/reference/index.html#core-features
https://docs.spring.io/spring/docs/
https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/index.html
https://www.cnblogs.com/diegodu/p/8794857.html
Spring WebFlux 入门的更多相关文章
- (转)Spring Boot 2 (十):Spring Boot 中的响应式编程和 WebFlux 入门
http://www.ityouknow.com/springboot/2019/02/12/spring-boot-webflux.html Spring 5.0 中发布了重量级组件 Webflux ...
- Spring Boot 2 (十):Spring Boot 中的响应式编程和 WebFlux 入门
Spring 5.0 中发布了重量级组件 Webflux,拉起了响应式编程的规模使用序幕. WebFlux 使用的场景是异步非阻塞的,使用 Webflux 作为系统解决方案,在大多数场景下可以提高系统 ...
- 【Java】Spring快速入门(一)
Spring介绍 Spring可以轻松创建Java企业应用程序.它提供了在企业环境中使用Java语言所需的一切,支持Groovy和Kotlin作为JVM上的替代语言,并可根据应用程序的需要灵活地创建多 ...
- 【MVC】Spring WebFlux
一.什么是 Spring WebFlux 下图截自 Spring Boot 官方网站: 结合上图,在了解 Spring WebFlux 之前,我们先来对比说说什么是 Spring MVC,这更有益我们 ...
- spring快速入门(四)
一.在spring快速入门(三)的基础上,我们来了解BeanFactory及配置. Client package com.murong.client; import org.springframewo ...
- spring快速入门(三)
一.在spring快速入门(二)的基础上,原先我们是采用构造方法完成对象的注入.这里还有其他的方法可以完成注入,通过set方法来完成. 修改UserActionImpl package com.mur ...
- spring快速入门(二)
一.在spring快速入门(一)的基础上,我们来了解spring是如何解决对象的创建以及对象之间的依赖关系的问题 (比如client中依赖UserAction的具体实现,UserActionImpl中 ...
- Spring快速入门
什么是Spring Spring是分层的JavaSE/EE full-stack(一站式) 轻量级开源框架 分层 SUN提供的EE的三层结构:web层.业务层.数据访问层(持久层/集成层) Strut ...
- 161103、Spring Boot 入门
Spring Boot 入门 spring Boot是Spring社区较新的一个项目.该项目的目的是帮助开发者更容易的创建基于Spring的应用程序和服务,让更多人的人更快的对Spring进行入门体验 ...
随机推荐
- HTML5图片预览 放大
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 2——PHP defined()函数
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- centos安装图形界面通常有两种方式
centos安装图形界面通常有两种方式 1.通过系统安装,在配置选择软件界面,选择GNOME桌面模式.
- 一文看懂js中元素的滚动大小(scrollWidth,scrollHeight,scrollTop,scrollLeft)
滚动大小(scroll dimension) 滚动大小指的是包含滚动内容元素的大小. 以下是与元素滚动内容大小相关的属性: 1. scrollWidth:在没有滚动条的情况下,元素内容的总宽度. 2. ...
- 【Amaple教程】6. 路由配置
在 第1节<启动路由> 章节中为了能让单页应用顺利跑起来,我们提前介绍了简单的路由配置方法.我们已了解路由配置的目的是指定不同的url下对应的 模块节点(也叫做模块容器)内应该显示哪个模块 ...
- vue 实现 裁切图片 同时有放大、缩小、旋转功能
实现效果: 裁切指定区域内的图片 旋转图片 放大图片 输出bolb 格式数据 提供给 formData 对象 效果图 大概原理: 利用h5 FileReader 对象, 获取 <input ty ...
- 前端每日实战:85# 视频演示如何用纯 CSS 创作一个小球反弹的动画
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/OwWROO 可交互视频 此视频是可 ...
- LeetCode 33.Search in Rotated Sorted Array(M)
题目: Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. ( ...
- linux最常用命令记录(一)
一.vim个人最常用设置: vim .vimrc 然后添加以下内容 set nu set tabstop=4 set encoding=utf-8 二.查看磁盘空间相关命令 1.df -h 查看硬 ...
- 程序员过关斩将-- 喷一喷坑爹的面向UI编程
摒弃面向UI编程 为何喷起此次话题,因为前不久和我们首席架构师沟通,谈起程序设计问题,一不小心把UI扯进来,更把那些按照UI来编程的后台工程师也扯了进来.今天特意百度了一下(其实程序员应该去googl ...