【Spring 5】响应式Web框架实战(上)
来源:互联网 发布:我想做淘宝兼职怎么做 编辑:程序博客网 时间:2024/06/11 04:42
引子:被誉为“中国大数据第一人”的涂子沛先生在其成名作《数据之巅》里提到,摩尔定律、社交媒体、数据挖掘是大数据的三大成因。IBM的研究称,整个人类文明所获得的全部数据中,有90%是过去两年内产生的。在此背景下,包括NoSQL,Hadoop, Spark, Storm, Kylin在内的大批新技术应运而生。其中以RxJava和Reactor为代表的响应式(Reactive)编程技术针对的就是经典的大数据4V定义(Volume,Variety,Velocity,Value)中的Velocity,即高并发问题,而在即将发布的Spring 5中,也引入了响应式编程的支持。在接下来的几周,我会围绕响应式编程分三期与你分享我的一些学习心得。本篇是第三篇,通过一个简单的Spring 5示例应用,探一探即将于下月底发布的Spring 5的究竟。
前情概要:
- 【Spring 5】响应式Web框架前瞻
- 响应式编程总览
1 回顾
通过前两篇的介绍,相信你对响应式编程和Spring 5已经有了一个初步的了解。下面我将以一个简单的Spring 5应用为例,介绍如何使用Spring 5快速搭建一个响应式Web应用(以下简称RP应用)。
2 实战
2.1 环境准备
首先,从GitHub下载我的这个示例应用,地址是https://github.com/emac/spring5-features-demo。
然后,从MongoDB官网下载最新版本的MongoDB,然后在命令行下运行mongod &
启动服务。
现在,可以先试着跑一下项目中自带的测试用例。
./gradlew clean build
2.2 依赖介绍
接下来,看一下这个示例应用里的和响应式编程相关的依赖。
compile('org.springframework.boot:spring-boot-starter-webflux')compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')testCompile('io.projectreactor.addons:reactor-test')
- spring-boot-starter-webflux: 启用Spring 5的RP(Reactive Programming)支持,这是使用Spring 5开发RP应用的必要条件,就好比spring-boot-starter-web之于传统的Spring MVC应用。
- spring-boot-starter-data-mongodb-reactive: Spring 5中新引入的针对MongoDB的Reactive Data扩展库,允许通过统一的RP风格的API操作MongoDB。
- io.projectreactor.addons:reactor-test: Reactor(Spring 5默认使用的RP框架)提供的官方测试工具库。
2.3 示例代码
不知道你是否还记得,在本系列第一篇【Spring 5】响应式Web框架前瞻里提到,Spring 5提供了Spring MVC注解和Router Functions两种方式来编写RP应用。本篇我就先用大家最熟悉的MVC注解来展示如何编写一个最简单的RP Controller。
@RestControllerpublic class RestaurantController { /** * 扩展ReactiveCrudRepository接口,提供基本的CRUD操作 */ private final RestaurantRepository restaurantRepository; /** * spring-boot-starter-data-mongodb-reactive提供的通用模板 */ private final ReactiveMongoTemplate reactiveMongoTemplate; public RestaurantController(RestaurantRepository restaurantRepository, ReactiveMongoTemplate reactiveMongoTemplate) { this.restaurantRepository = restaurantRepository; this.reactiveMongoTemplate = reactiveMongoTemplate; } @GetMapping("/reactive/restaurants") public Flux<Restaurant> findAll() { return restaurantRepository.findAll(); } @GetMapping("/reactive/restaurants/{id}") public Mono<Restaurant> get(@PathVariable String id) { return restaurantRepository.findById(id); } @PostMapping("/reactive/restaurants") public Flux<Restaurant> create(@RequestBody Flux<Restaurant> restaurants) { return restaurants .buffer(10000) .flatMap(rs -> reactiveMongoTemplate.insert(rs, Restaurant.class)); } @DeleteMapping("/reactive/restaurants/{id}") public Mono<Void> delete(@PathVariable String id) { return restaurantRepository.deleteById(id); }}
可以看到,实现一个RP Controller和一个普通的Controller是非常类似的,最核心的区别是,优先使用RP中最基础的两种数据类型,Flux
(对应多值)和Mono
(单值),尤其是方法的参数和返回值。即便是空返回值,也应封装为Mono<Void>
。这样做的目的是,使得应用能够以一种统一的符合RP规范的方式处理数据,最理想的情况是从最底层的数据库(或者其他系统外部调用),到最上层的Controller层,所有数据都不落地,经由各种Flux
和Mono
铺设的“管道”,直供调用端。就像农夫山泉那句著名的广告词,我们不生产水,我们只是大自然的搬运工。
2.4 单元测试
和非RP应用的单元测试相比,RP应用的单元测试主要是使用了一个Spring 5新引入的测试工具类,WebTestClient
,专门用于测试RP应用。
@RunWith(SpringRunner.class)@SpringBootTestpublic class RestaurantControllerTests { @Test public void testNormal() throws InterruptedException { // start from scratch restaurantRepository.deleteAll().block(); // prepare WebTestClient webClient = WebTestClient.bindToController(new RestaurantController(restaurantRepository, reactiveMongoTemplate)).build(); Restaurant[] restaurants = IntStream.range(0, 100) .mapToObj(String::valueOf) .map(s -> new Restaurant(s, s, s)) .toArray(Restaurant[]::new); // create webClient.post().uri("/reactive/restaurants") .accept(MediaType.APPLICATION_JSON_UTF8) .syncBody(restaurants) .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8) .expectBodyList(Restaurant.class) .hasSize(100) .consumeWith(rs -> Flux.fromIterable(rs) .log() .subscribe(r1 -> { // get webClient.get() .uri("/reactive/restaurants/{id}", r1.getId()) .accept(MediaType.APPLICATION_JSON_UTF8) .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8) .expectBody(Restaurant.class) .consumeWith(r2 -> Assert.assertEquals(r1, r2)); }) ); }}
创建WebTestClient
实例时,首先要绑定一下待测试的RP Controller。可以看到,和业务类一样,编写RP应用的单元测试,同样也是数据不落地的流式风格。
在示例应用中可以找到更多的单元测试。
3 小结
以上就是Spring 5里第一种,相信也将会是最常用的编写RP应用的实现方式。介于篇幅原因,这篇就先到这里。下篇我将详细介绍第二种方式,Router Functions。欢迎你到我的留言板分享,和大家一起过过招。
4 参考
- Spring Framework Reference - WebFlux framework
- spring-framework Reactive Tests
- 【Spring 5】响应式Web框架实战(上)
- 【Spring 5】响应式Web框架实战(下)
- 【Spring 5】响应式Web框架前瞻
- 响应式Web设计:HTML5和CSS3实战(读书笔记)
- 实战-web项目集成spring框架
- 响应式web css框架集合
- 十大响应式Web设计框架
- 十大响应式Web设计框架
- 大响应式Web设计框架
- Spring 5 新功能:函数式 Web 框架
- Spring 5 新功能:函数式 Web 框架
- Java web 之 Spring+Mybatis+Spring MVC框架整合(上)
- 响应式开发从原理到实战案例(六):bootstrap框架介绍
- 响应式web设计 HTML5+css3实战 学习笔记
- 响应式Web设计:HTML5和CSS3实战
- 响应式Web设计:HTML5和CSS3实战 pdf版
- 读高性能响应式web开发实战有感
- Spring MVC实战系列教程(3)--响应方式
- shiro的过虑器
- C#练习——用方法实现两个数的最大值
- ASP.NET HttpContext.Current在新建线程中会返回null
- Win 10 + Ubuntu 16.04 双系统卸载Ubuntu
- NYOJ:68-三点顺序
- 【Spring 5】响应式Web框架实战(上)
- iOS APP启动原理及视图~详解
- NYOJ:69-数的长度
- NYOJ:70-阶乘因式分解(二)
- Leetcode413. Arithmetic Slices
- 使用caffe fine-tune一个单标签图像分类模型
- JavaScript本地对象之系列:Array对象
- 百练 实现堆结构
- 居然现在才安装eclipse,还来得及?