Spring Framework 5.0 M5 更新

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

Spring Framework 5.0 第 5 个也是最后一个里程碑版本的更新...

Spring MVC 和 Spring WebFlux

“Spring MVC”这个名字非常熟悉且被广泛使用,但也许会让一些人惊讶的是,实际上并没有一个名为“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 的其余部分是命令式的,不能在响应式、无阻塞的堆栈中使用。

到目前为止,我们一直缺乏一个专门的名称来描述响应式 Web 堆栈,它支持 Spring MVC 注解(例如 `@Controller`、`@RequestMapping`)以及新的函数式编程模型,这使得讨论和清晰地对比编程模型和堆栈变得具有挑战性。在我们的里程碑 5 中,`spring-web-reactive` 模块被重命名为 `spring-webflux`——从 Spring Web Reactive API 核心的 Flux 响应式类型中汲取灵感并保持简洁,而我们的低级响应式 HTTP 抽象则位于通用的 `spring-web` 模块中。因此,现在我们有了 `spring-webmvc` 和 `spring-webflux` 这两个模块并排存在,我们将分别称它们为Spring (Web) MVCSpring WebFlux。如果您想知道:新模块的顶级包是 `org.springframework.web.reactive`。

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

WebClient

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

在 M5 中,我们进行了实质性的改进,消除了在指定请求详细信息和执行交换时对静态导入的需求。

WebClient webClient = WebClient.create();

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

如果所有请求都有一个通用的基本 URL,它可以一次性预先配置。

WebClient webClient = WebClient.create("https://: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("https://: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();

路径模式解析器

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 社区所有即将举行的活动。

查看所有