全国咨询/投诉热线:400-618-4000

spring5.0响应式编程入门

更新时间:2018年12月07日13时28分 来源:传智播客 浏览次数:

Spring5.0响应式编程入门

引言

响应式编程是一种面向数据流和变化传播的编程范式。使用它可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。我们可以使用声明的方式构建应用程序的能力,形成更加敏感和有弹性的应用,所以Spring 5在其核心框架中增加了反应系统,已经开始向声明式编程的范式转变。

响应式编程的优势

• 提高了代码的可读性,因此开发人员只需要关注定义业务逻辑。

• 在高并发环境中,可以自然的处理消息。

• 可以控制生产者和消费者之间的流量,避免内存不足。

• 对于一个或多个线程,IO绑定任务可以通过异步和非阻塞方式执行,而且不阻塞当前线程。

• 可以有效的管理多个连接系统之间的通信。

应用场景

• 大量的交易处理服务,如银行部门。

• 大型在线购物应用程序的通知服务,如亚马逊。

• 股票价格同时变动的股票交易业务。

Spring 5.0前瞻

作为Java中的首个响应式Web框架,Spring 5.0最大的亮点莫过于提供了完整的端到端响应式编程的支持。

Spring5.0响应式编程入门

如上图所示左侧是传统的基于Servlet的Spring Web MVC框架,右侧是spring 5.0新引入的基于Reactive Streams的Spring WebFlux框架,从上往下依次是:Router Functions,WebFlux,Reactive Streams三个新组件,其中:

• Router Functions: 对标@Controller,@RequestMapping等标准的Spring MVC注解,提供一套函数式风格的API,用于创建Router,Handler和Filter。

• WebFlux: 核心组件,协调上下游各个组件提供响应式编程支持。

• Reactive Streams: 一种支持背压(Backpressure)的异步数据流处理标准,主流实现有RxJava和Reactor,Spring WebFlux默认集成的是Reactor。

示例代码

1.创建项目

spring响应式开发,需要结合spring boot来完成,所以使用idea创建springboot项目,选择Spring Initializr选项,jdk选择1.8以上。

Spring5.0响应式编程入门

选择下一步,设置groupId和artifactId。

Spring5.0响应式编程入门

选择下一步,选择web选项中的Reactive web选项,表明要创建响应式项目,注意Spring Boot的版本为2.0.2。

spring编程入门

选择下一步,设置项目的目录,点击finish创建项目。

Spring5.0响应式编程入门

2.设置服务端口

为了避免端口冲突,我们可以在项目的application.properties文件中配置服务端口。

server.port=9002

3.相关依赖包

打开项目的pom.xml文件,会发现以下几个依赖包

org.springframework.boot

spring-boot-starter-webflux

org.springframework.boot

spring-boot-starter-test

test

io.projectreactor

reactor-test

test

其中:

• spring-boot-starter-webflux:webflux依赖包,是响应式开发的核心依赖包,其中包含了spring-boot-starter-reactor-netty 、spring 5 webflux 包,默认是通过netty启动的。

• spring-boot-starter-test:springboot的单元测试工具库。

• reactor-test:Spring 5提供的官方针对RP框架测试工具库。

小结

spring响应式开发需要结合spring boot来完成,同时需要引入spring-boot-starter-webflux和reactor-test两个依赖包来支持响应式开发。

4.使用webflux创建web应用

webflux的使用有两种方式,基于注解和函数式编程。这里使用函数式编程,具体操作如下:

4.1.创建实体类

public class Good {

private int id;

private String name;

private String price;

public Good(int id,String name,String price){

this.id=id;

this.name=name;

this.price=price;

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getPrice() {

return price;

}

public void setPrice(String price) {

this.price = price;

}

@Override

public String toString() {

return "Good{" +

"id=" + id +

", name='" + name + '\'' +

", price='" + price + '\'' +

'}';

}

}

分析: 实体类没有什么特殊的操作,原来怎么操作现在还是怎么操作。

4.2.创建GoodGenerator

@Configuration

public class GoodGenerator {

public Flux findGoods(){

List goods = new ArrayList<>();

goods.add(new Good(1,"小米","2000"));

goods.add(new Good(2,"华为","4000"));

goods.add(new Good(3,"苹果","8000"));

return Flux.fromIterable(goods);

}

}

分析: 这里的方法返回的是Flux类型的数据,Flux是RP中最基础的数据类型之一,对应的是多值数据的返回操作,在RP中还有Mono数据类型,也是最基础的数据类型之一,对应单值数据的返回操作。

注意:这里的GoodGenerator类要加上@Configuration注解。

4.3.创建GoodHandler

@Component

@Configuration

public class GoodHandler {

private final Flux goods;

public GoodHandler(GoodGenerator goodGenerator) {

this.goods = goodGenerator.findGoods();

}

public Mono hello(ServerRequest request) {

return ok().contentType(TEXT_PLAIN)

.body(BodyInserters.fromObject("Hello Spring!"));

}

public Mono echo(ServerRequest request) {

return ok().contentType(APPLICATION_STREAM_JSON)

.body(this.goods,Good.class);

}

}

分析: Handler主要用来处理请求操作,并将Mono返回,Mono中会封装响应数据,响应数据如果是字符串可以使用:

ok().contentType(TEXT_PLAIN).body(BodyInserters.fromObject("Hello Spring!"));

操作,如果是集合数据可以使用:

ok().contentType(APPLICATION_STREAM_JSON).body(this.goods,Good.class)

操作。

4.4.创建GoodRouter

@Configuration

public class GoodRouter {

@Bean

public RouterFunction route(GoodHandler goodHandler) {

return RouterFunctions

.route(RequestPredicates.GET("/good")

.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),goodHandler::hello)

.andRoute(RequestPredicates.GET("/goods")

.and(RequestPredicates.accept(MediaType.APPLICATION_STREAM_JSON)),goodHandler::echo);

}

}

分析: GoodRouter主要用来设置请求路径和转化HTTP请求,可以使用route()方法和andRoute方法设置多个请求路径和转化操作。

小结

HTTP请求会由GoodRouter转发给对应的Handler,Handler处理请求,并返回Mono,这里的Router类似@RequestMapping,Handler类似Controller

4.4.运行测试

实体类、GoodGenerator、GoodHandler、GoodRouter都已经创建完成了,我们可以运行项目打开浏览器进行测试.

浏览器输入http://localhost:9002/good,即可获取到"Hello Spring!"文本信息

浏览器输入http://localhost:9002/goods,即可获取到集合信息

到目前为止,一个简单的webflux示例已经完成。

4.5.单元测试

在项目中我们也可以使用使用一个Spring 5新引入的测试工具类,WebTestClient,专门用于测试RP应用,具体代码如下:

@RunWith(SpringRunner.class)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

public class Spring5demoApplicationTests {

@Autowired

private WebTestClient webTestClient;

@Test

public void helloTest() {

String s= webTestClient

.get().uri("/good")

.accept(MediaType.TEXT_PLAIN).exchange()

.expectStatus().isOk().returnResult(String.class)

.getResponseBody().blockFirst();

System.out.println(s);

}

@Test

public void findGoodsTest(){

webTestClient.get().uri("/goods")

.accept(MediaType.APPLICATION_STREAM_JSON)

.exchange().expectStatus().isOk()

.expectHeader().contentType(MediaType.APPLICATION_STREAM_JSON)

.returnResult(Good.class)

.getResponseBody().collectList();

}

}

创建WebTestClient实例时可以看到,编写RP应用的单元测试,同样也是数据不落地的流式风格

总结

到此,spring 5.0的响应式编程就给大家介绍到这里,这里只是简单进行了一个响应式入门操作,但是也能够体现出响应式编程的特点。当然spring 5.0响应式编程也不是完美的,它在故障诊断、依赖库集成、数据存储以及Spring Security安全权限框架支持等方面还是有局限性的。

javaee

python

web

ui

cloud

test

c

netmarket

pm

Linux

movies

robot

uids

北京校区

    14天免费试学

    基础班入门课程限时免费

    申请试学名额

    15天免费试学

    基础班入门课程限时免费

    申请试学名额

    15天免费试学

    基础班入门课程限时免费

    申请试学名额

    15天免费试学

    基础班入门课程限时免费

    申请试学名额

    20天免费试学

    基础班入门课程限时免费

    申请试学名额

    8天免费试学

    基础班入门课程限时免费

    申请试学名额

    20天免费试学

    基础班入门课程限时免费

    申请试学名额

    5天免费试学

    基础班入门课程限时免费

    申请试学名额

    0天免费试学

    基础班入门课程限时免费

    申请试学名额

    12天免费试学

    基础班入门课程限时免费

    申请试学名额

    5天免费试学

    基础班入门课程限时免费

    申请试学名额

    5天免费试学

    基础班入门课程限时免费

    申请试学名额

    10天免费试学

    基础班入门课程限时免费

    申请试学名额