关于Spring 5中的反应式编程支持Reactor类库,上一篇文章《
Spring Boot 实践折腾记(10):2.0+版本中的反应式编程支持——Reactor》已经简要介绍过,Spring 5 框架所包含的内容很多,本文还会继续介绍其中新增的 WebFlux 模块。开发人员可以使用 WebFlux 创建高性能的 Web 应用和客户端。然后,我们再结合Spring Boot 2中对Spring 5的支持,来快速构建一个响应式的Web API。

WebFlux 简介

WebFlux 模块的名称是 spring-webflux,基础类库其实还是来源于 Reactor 中对Reactive Streams规范的实现。该模块中包含了对反应式 HTTP、服务器推送事件和 WebSocket 的客户端和服务器端的支持。

WebFlux 需要底层提供运行时的支持,WebFlux 可以运行在支持 Servlet 3.1 非阻塞 IO API 的 Servlet 容器上,或是其他异步运行时环境,如 Netty 和 Undertow。Spring Boot 2默认选择的是Netty作为非阻塞 IO API 的 Servlet 容器。

在服务器端,WebFlux支持两种不同的编程模型:

  1. Spring MVC 中使用的基于 Java 注解的方式;
  2. 基于 Java 8 的 lambda 表达式的函数式编程模型。

这两种编程模型只是在代码编写方式上存在不同。它们运行在同样的反应式底层架构之上,因此在运行时是相同的,层级关系如下图所示:


要注意的是,适用于反应式的HTTP请求响应——ServerHttpRequest和ServerHttpResponse,会将请求和响应的正文转换为Flux ,而不是InputStream和OutputStream。而REST风格的JSON和XML,在序列化和反序列化时也会将正文转换为为Flux ,同理,HTML的视图渲染和服务器发送事件也会进行相应转换。

实战

下面我们通过几个简单的例子来上手实战一下响应式的API开发,Spring Boot 2已经集成了反应式的支持,我们只需要使用spring-boot-starter-webflux的启动器POM,它提供了默认为Netty支持的Spring WebFlux。 有关详细信息,还可以查看Spring Boot参考文档。

Java 注解编程模型

基于注解的编程范式,对于Java开发人员来说是非常熟悉和简单的,而在反应式编程里,同样很快就能上手,我们来看一个简单的Hello World例子。

StartAppFlux代码:

@SpringBootApplication
@RestController
public class StartAppFlux {public static void main(String[] args) {SpringApplication.run(StartAppFlux.class,args);}@GetMapping("/hello_world_mono")public Mono<String> helloWorld() {return Mono.just("Hello World with flux's mono");}@GetMapping("/hello_world_flux")public Flux<String> helloWorldFlux() {return Flux.just("Hello", "World");}
}

REST API

简单的例子只是刚开始,实际应用中,开发API可能才是更重要的工作之一。我们来实战一个简单的例子。该 REST API 用来对用户数据进行基本的 CRUD 操作。POJO对象Man有四个基本属性id、name、age、phone。用于操作对象的服务QueryUserServices,使用一个Map来模拟数据库的读写操作。

Man对象代码:

public class Man {private String id;private String name;private int age;private String phone;public Man(String id, String name, int age, String phone) {this.id = id;this.name = name;this.age = age;this.phone = phone;}
//省略get、set
}

QueryUserServices代码 :

@Service
public class QueryUserServices {private final Map<String, Man> data = new ConcurrentHashMap<>();@PostConstructvoid init(){Man man1 = new Man("1","mickjoust",66,"21313123132");Man man2 = new Man("2","mia",66,"21313123132");Man man3 = new Man("3","max",66,"21313123132");data.put(man1.getId(),man1);data.put(man2.getId(),man2);data.put(man3.getId(),man3);}Flux<Man> list() {return Flux.fromIterable(this.data.values());}Flux<Man> getById(final Flux<String> ids) {return ids.flatMap(id -> Mono.justOrEmpty(this.data.get(id)));}Mono<Man> getById(final String id) {return Mono.justOrEmpty(this.data.get(id)).switchIfEmpty(Mono.error(new RuntimeException()));}Flux<Man> createOrUpdate(final Flux<Man> mans) {return mans.doOnNext(man -> this.data.put(man.getId(), man));}Mono<Man> createOrUpdate(final Man man) {this.data.put(man.getId(), man);return Mono.just(man);}Mono<Man> delete(final String id) {return Mono.justOrEmpty(this.data.remove(id));}
}

WebFluxController代码:

@RestController
@RequestMapping("/")
@SpringBootApplication
public class WebFluxController {@Autowiredprivate QueryUserServices queryUserServices;public static void main(String[] args) {SpringApplication.run(WebFluxController.class,args);}@GetMapping("/get_flux")public Flux<Man> getFlux(){return queryUserServices.list();}@GetMapping("/get_flux_ids/{ids}")public Flux<Man> getFluxById(@PathVariable("ids") final String idStr){//逗号隔开,模拟,实际可以直接传post请求String[] testStr = idStr.split(",");Flux<String> ids = Flux.fromArray(testStr);return queryUserServices.getById(ids);}@GetMapping("/get_mono/{id}")public Mono<Man> getMono(@PathVariable("id") final String id){return queryUserServices.getById(id);}@PostMapping("/create_flux")public Flux<Man> createFlux(@RequestBody final Flux<Man>  mans){return queryUserServices.createOrUpdate(mans);}@PostMapping("/update_mono/{id}")public Mono<Man> updateMono(@PathVariable("id") final String id, @RequestBody final Man man){Objects.requireNonNull(man);man.setId(id);return queryUserServices.createOrUpdate(man);}@GetMapping("/delete_mono/{id}")public Mono<Man> delete(@PathVariable("id") final String id){return queryUserServices.delete(id);}}

函数式编程模型

在上节中介绍了基于 Java 注解的编程模型,WebFlux 还支持另一种基于 lambda 表达式的函数式编程模型。与基于 Java 注解的编程模型相比,函数式编程模型的抽象层次更低,代码编写更灵活,可以满足一些对动态性要求更高的场景。不过在编写时的代码复杂度也较高,学习曲线也较陡。开发人员可以根据实际的需要来选择合适的编程模型。目前 Spring Boot 不支持在一个应用中同时使用两种不同的编程模式。

客户端

除了服务器端实现之外,WebFlux 也提供了反应式客户端,可以访问 HTTP、SSE 和 WebSocket 服务器端。

HTTP

对于 HTTP 和 SSE,可以使用 WebFlux 模块中的类 org.springframework.web.reactive.function.client.WebClient。如下代码中的 RESTClient 用来访问前面小节中创建的 REST API。首先使用 WebClient.create 方法来创建一个新的 WebClient 对象,然后使用方法 post 来创建一个 POST 请求,并使用方法 body 来设置 POST 请求的内容。方法 exchange 的作用是发送请求并得到以 Flux表示的 HTTP 响应。最后对得到的响应进行处理并输出结果。response 的 bodyToMono 方法把响应内容转换成类 Man的对象,最终得到的结果是 Flux对象。调用 createdUser.blockFirst() 方法的作用是等待请求完成并得到所产生的类 Man 的对象。

RESTClient代码:

public class RESTClient {public static void main(final String[] args) {Man man = new Man();man.setId("11");man.setAge(33);man.setName("Test");man.setPhone("1232312313");WebClient client = WebClient.create("http://localhost:8080/create_flux");Flux<Man> createdUser = client.post().uri("").accept(MediaType.APPLICATION_JSON).body(Mono.just(man), Man.class).exchange().flatMapMany(response -> response.bodyToFlux(Man.class));System.out.println(createdUser.blockFirst());//会报错Exception in thread "main" java.lang.NoClassDefFoundError: reactor/core/CoreSubscriber}
}

遗留问题

本来想加上函数式编程,但感觉信息量很大,后续单独加一篇文章来进行实战,同时,在客服端一节中的测试代码WebClient,会报异常,目前暂时未解决,导致了测试本身运行有点问题,后续文章解决后再更新。

小结

本文对 WebFlux 模块进行了简要介绍,主要是其中的 HTTP 支持。对于Sprng 5新增的WebFlux模块,基于Java注解模型的编程范式,是对反应式模型的一种实现,对于需要进行反应式的开发场景有很好的支持。

参考资源

1、webFlux:https://docs.spring.io/spring/docs/5.0.0.RC2/spring-framework-reference/web.html#web-reactive
2、Reactive Streams:https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams


我的其它穿越门——持续践行,我们一路同行。
头条号:说言风语
简书ID:mickjoust
公号:说言风语

Spring Boot 实践折腾记(11):使用 Spring 5的WebFlux快速构建效响应式REST API相关推荐

  1. Spring Boot 实践折腾记(20):Thymleaf + webjar + ECharts 构建本地图表

    前言 作为geek后端程序员,画图的选择其实有很多,手画,excel,ppt,copy别人的图等.虽然用excel最方便,但是类似按中国省份来显示详细数据的图,excel是画不了的,而PPT画起来又很 ...

  2. Spring Boot 实践折腾记(13):使用WebFlux构建响应式「推送API 」

    西方著名宗教史家米尔恰·伊利亚德说,如果今天我们不生活在未来,那么未来,我们会生活在过去. 随着移动互联网的普及,基于C/S模式的APP开始了它的又一轮爆发式增长,过去,我们总说要去C/S模式,拥抱B ...

  3. Spring Boot 实践折腾记(四):配置即使用,常用配置

    生活不可能像你想象的那么好,但也不会像你想象的那么糟.我觉得人的脆弱和坚强都超乎自己的想象.有时,可能脆弱得一句话就泪流满面:有时,也发现自己咬着牙走了很长的路.--源自 莫泊桑 开始前- 本章内容主 ...

  4. Spring Boot 实践折腾记(15):使用Groovy

    Java是在JVM上运行的最广泛使用的编程语言.不过,还有很多其他基于JVM的语言,比如Groovy,Scala,JRuby,Jython,Kotlin等等.其中,Groovy和Scala现在在Jav ...

  5. Spring Boot 实践折腾记(10):响应式编程支持库Reactor

    Spring Boot 2.0发布已经过去了2个多月,随着微服务的流行,Spring Boot也越来越受到青睐,更好的隔离编程范式得到了越来越多项目的应用,这是一件值得高兴的事. Spring Boo ...

  6. Spring Boot 实践折腾记(12):支持数据缓存Cache

    不管是什么类型的应用程序,都离不开数据,即便如现在的手机APP,我们依然需要使用数数据库,对于不懂的人,当然,我们可以告诉他们一些高大上的概念,但是作为专业人士,就一定要明白背后的真实原理到底是什么. ...

  7. Spring Boot 实践折腾记(16):使用Scala

    英国作家萨缪尔·约翰逊曾说过,成大事不在于力量的大小,而在于能坚持多久. 前面我们介绍了如何在Spring Boot下使用Kotlin和Groovy,还有一个热门语言是Scala,做大数据的同学对Sc ...

  8. Spring Boot基础学习笔记11:Spring MVC

    文章目录 零.学习目标 一.Spring MVC 自动配置 (一)自动配置概述 (二)Spring Boot整合Spring MVC 的自动化配置功能特性 二.Spring MVC 功能拓展实现 (一 ...

  9. Spring Boot实践

    Spring Boot实践 在本文中,我将重点介绍Spring Boot特有的实践(大多数时候,也适用于Spring项目).以下依次列出了最佳实践,排名不分先后. 1.使用自定义BOM来维护第三方依赖 ...

最新文章

  1. C++ 中 string earse 函数的使用
  2. dubbo:9个你不一定知道,但好用的功能
  3. batch_size 和 fetch_size作用
  4. GetCursorPos/WindowFromPoint/SendMessage
  5. 许昌:让便捷支付成为智慧城市新名片
  6. timeval的时间转换成毫秒之后多大的数据类型可以装下
  7. java异常中的Error和Exception的区别是什么?
  8. nginx 隐藏目录_Nginx学习之简单练习反向代理和负载均衡
  9. linux 两个mysql_Linux 安装两个MySQL
  10. c语言小游戏如何编写,如何用c语言编写小游戏.docx
  11. 计算机网络知识点汇总
  12. div、p、span标签如何做到首行缩进两个字符
  13. 5分钟使用Unity制作AR应用,结合EasyAR制作AR(转)
  14. 轨迹(形状)相似性判断与度量方法
  15. 【USB】USB video class (UVC)相关概念学习
  16. Water Research | 南科大夏雨组揭示Anammox菌群微米级空间异质性和保守互作
  17. 看完这几道 JavaScript 面试题,让你与考官对答如流(中)
  18. C语言_指针动态内存分布
  19. 软通公司招聘NLP研发工程师一名
  20. Filament加载网络gltf文件

热门文章

  1. 【zzulioj 1897 985的红绿灯难题】
  2. 大数据时代 你的生活被“格式化”了没
  3. 分布式服务器架构搭建和部署
  4. 【BZOJ3213】抛硬币(ZJOI2013)-期望DP+KMP+高精度
  5. 0X80000000和0X7FFFFFFF
  6. 配置zsh终端打开VsCode
  7. 【编程数学】001 整除与被整除、除与除以、整数
  8. windows真正体验3D效果桌面
  9. inittab流程解析
  10. 大牛分享,献出这份年薪68W的蚂蚁金服Java高级开发封神宝典!