Spring Framework 5.0 M5 更新

工程 | Rossen Stoyanchev | 2017年2月23日 | ...

关于 Spring Framework 5.0 第五个也是最后一个里程碑的更新...

Spring MVC 和 Spring WebFlux

`Spring MVC` 这个名字广为人知且被广泛使用,但可能令一些人惊讶的是,并没有一个实际的项目或独立的发行版以此命名。它实际上是 Spring Framework 发行版中的一个模块,称为 `spring-webmvc`。这里还有一个小知识问题。你知道这个模块的顶层包名中不包含“mvc”吗?它叫做 `org.springframework.web.servlet`。实际上,这些细节我们不必记住。重要的是,我们有一个简短易记的名字来指代 *Spring 基于 Servlet 栈* 的 Web 框架。

Spring 的反应式栈 Web 框架是 5.0 版本中的新特性,它是完全反应式且非阻塞的。它适用于使用少量线程进行事件循环风格的处理。它不仅支持 Servlet 容器(Tomcat, Jetty, Servlet 3.1+),也支持非 Servlet 运行时(Netty, Undertow),因为这个栈的共同基础不是 Servlet API,而是基于 Reactive Streams 和 Reactor 项目构建的非阻塞替代方案。如果你想知道,Servlet 3.1 不是支持非阻塞 I/O 吗?是的,我们支持在 Servlet 3.1 容器上运行,但 Servlet API 的其余部分是命令式风格的,不能在反应式、非阻塞的栈中使用。

迄今为止,我们一直缺乏一个专门的名称来指代既支持 Spring MVC 注解(即 @Controller, @RequestMapping)又支持新函数式编程模型的反应式 Web 栈,这使得讨论和清晰对比各种编程模型和栈变得具有挑战性。在我们的里程碑 5 中,spring-web-reactive 模块被重命名为 spring-webflux——灵感和简洁性来自于 Spring Web Reactive API 核心的 Flux 反应式类型,而我们较低级别的反应式 HTTP 抽象则位于通用的 spring-web 模块中。因此,我们现在有了并存的 spring-webmvcspring-webflux 模块,我们将分别称之为 *Spring (Web)MVC* 和 *Spring WebFlux*。如果你想知道,新模块的顶层包是 org.springframework.web.reactive

接下来是这个领域的一些其他重要更新...

WebClient

`WebClient` 是 `RestTemplate` 的反应式、非阻塞替代方案,为*反应式*和*Servlet 栈*应用都带来了价值。它使得处理异步和流式场景变得轻而易举。

在 M5 中,我们进行了重大改进,消除了指定请求详情和执行交换时对静态导入的需要

WebClient webClient = WebClient.create();

Mono<Person> person = webClient.get()
        .uri("http://localhost:8080/persons/42")
        .accept(MediaType.APPLICATION_JSON)
        .exchange()
        .then(response -> response.bodyToMono(Person.class));

如果所有请求都有一个共同的基础 URL,可以一次性预配置

WebClient webClient = WebClient.create("http://localhost:8080");

Mono<Person> person = webClient.get()
        .uri("/persons/{id}", 42)
        .accept(MediaType.APPLICATION_JSON)
        .exchange()
        .then(response -> response.bodyToMono(Person.class));

还可以使用 UriBuilder 获得编程控制能力

Mono<Person> person = webClient.get()
        .uri(builder -> builder.path("/persons/{id}").build("42"))
        .accept(MediaType.APPLICATION_JSON)
        .exchange()
        .then(response -> response.bodyToMono(Person.class));

WebTestClient

`spring-test` 模块中的新 `WebTestClient` 构成了 Spring WebFlux 集成测试支持的基础。它封装了 `WebClient` 并提供了一个用于集成测试的 API。与 Spring MVC Test 中的 `MockMvc` 类似,新的测试客户端不需要一个实际运行的服务器,而是可以使用模拟请求和响应直接绑定到 WebFlux 服务器基础设施:````java WebTestClient client = WebTestClient .bindToController(new PersonController()) .build();

client.get().uri("/persons/42") .exchange() .expectStatus().isOk() .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8) .expectBody(Person.class).value().isEqualTo(new Person("John"));


The new test client however can also run against a live server:
````java
WebTestClient client = WebTestClient
        .bindToServer().baseUrl("http://localhost:8080")
        .build();

// Same test case...

流式处理也很容易测试,可能使用 Reactor StepVerifier

FluxExchangeResult<Person> result = client.get().uri("/persons")
        .accept(TEXT_EVENT_STREAM)
        .exchange()
        .expectStatus().isOk()
        .expectHeader().contentType(TEXT_EVENT_STREAM)
        .expectBody(Person.class)
        .returnResult();

StepVerifier.create(result.getResponseBody())
        .expectNext(new Person("Jane"), new Person("Jason"))
        .expectNextCount(3)
        .consumeNextWith(p -> assertEquals("John", p.getName()))
        .thenCancel()
        .verify();

Path Pattern Parser

M5 版本新增了一个 `PathPatternParser`,作为 `AntPathMatcher` 的替代方案。它支持使用更高效的已解析模式表示来处理请求映射,并且支持非常方便的 `"{*foo}"` URI 变量语法,可以捕获模式末尾的任意数量的路径段。

作为第一步,一个新的 ParsingPathMatcher 实现已经允许轻松地将新的 PathPatternParser 插入到 Spring MVC 和 Spring WebFlux 的映射中。迈向 RC1 的下一步目标是建立一个模式注册表,用于与请求路径的已解析表示进行匹配。

Server-Sent Events 和 JSON 流

使用 Spring WebFlux 可以轻松支持流式传输:````java @GetMapping(path = "/persons", produces = "text/event-stream") FluxgetPersons() { return this.repository.getPersons(); } ```` 上述代码将以 Server-Sent Events (SSE) 格式向浏览器传输数据流:```` data: {"name":"Jane"} data: {"name":"John"} ... ````

但是对于

@GetMapping("/persons")
Flux<Person> getPersons() {
    return this.repository.getPersons();
}

我们可以流式传输单个 JSON 对象,但这作为一个整体将不是一个有效的 JSON 文档,并且浏览器客户端除了使用 Server-Sent Events 或 WebSocket 之外没有其他方式来消费流。

默认情况下,Flux<Person> 将生成一个 JSON 数组

[{"name":"Jane"},{"name":"John"},...]

非浏览器客户端(例如 WebClient)可以请求内容类型为 application/stream+json,响应将是一个 JSON 对象流,类似于 Server-Sent Events 但没有额外的格式。

{"name":"Jane"}
{"name":"John"}
...

总结

我要感谢所有尝试过 Spring Framework 5.0 并提供反馈的人,尤其是关于新的反应式特性。请大家继续这样做。一如既往,即使是小的评论也可能非常有价值,因为它们让我们从不同的角度重新审视设计选择。

如果你本周在 DevNexus 阅读这篇文章,不要错过机会观看 了不起的 Josh Long 现场编码一个反应式 Web 应用,也不要错过 Reactor 团队核心成员 Simon Baslé 关于 Reactor 3 的演讲

订阅 Spring 电子报

通过 Spring 电子报保持联系

订阅

抢占先机

VMware 提供培训和认证,助您飞速进步。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举办的活动

查看 Spring 社区中所有即将举办的活动。

查看全部