8.2.3 使用响应式流实现业务逻辑
Spring 响应式技术栈使构建异步、非阻塞应用程序变得简单直接。在上一节中,我们使用了 Spring Data R2DBC,不需要处理任何底层的响应式问题。这在 Spring 中的所有响应式模块中通常是正确的。作为开发人员,你可以依赖熟悉、简单和高效的方法来构建响应式应用程序,而框架负责所有繁重的工作。
默认情况下,Spring WebFlux 假设一切都是响应式的。这个假设意味着你应该通过交换 Publisher
在 com.polarbookshop.orderservice.order.domain 包中,创建一个新的 OrderService 类。首先,让我们实现通过仓库读取订单的逻辑。当涉及多个订单时,你可以使用 Flux
代码清单 8.12 通过响应式流获取订单
package com.polarbookshop.orderservice.order.domain;
import reactor.core.publisher.Flux;
import org.springframework.stereotype.Service;
@Service // 标记类为由 Spring 管理的服务
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public Flux<Order> getAllOrders() {
return orderRepository.findAll(); // 使用 Flux 发布多个订单 (0..N)
}
}
接下来,我们需要一个方法来提交订单。在与 Catalog Service 集成之前,我们总是可以默认拒绝提交的订单。OrderRepository 暴露了 ReactiveCrudRepository 提供的 save() 方法。你可以构建一个响应式流,将 Mono
给定一个标识书籍的 ISBN 和要订购的副本数量,你可以使用 Mono.just() 构建一个 Mono 对象,就像你使用 Stream.of() 构建 Java Stream 对象一样。区别在于响应式行为。
你可以使用 Mono 对象启动响应式流,然后依赖 flatMap() 运算符将数据传递给 OrderRepository。将以下代码添加到 OrderService 类中,完成业务逻辑实现。
代码清单 8.13 在提交订单请求时持久化被拒绝的订单
// ... 其他代码
public Mono<Order> submitOrder(String isbn, int quantity) {
return Mono.just(buildRejectedOrder(isbn, quantity)) // 从 "Order" 对象创建 "Mono"
.flatMap(orderRepository::save); // 将前一步异步生成的 Order 对象保存到数据库
}
public static Order buildRejectedOrder(String bookIsbn, int quantity) {
// 当订单被拒绝时,我们只指定 ISBN、数量和状态
// Spring Data 负责添加标识符、版本和审计元数据
return Order.of(bookIsbn, null, null, quantity, OrderStatus.REJECTED);
}
// ... 其他代码
map 与 flatMap
使用 Reactor 时,在 map() 和 flatMap() 运算符之间选择通常是一个困惑的来源。两个运算符都返回响应式流(Mono
或 Flux ),但 map() 在两个标准 Java 类型之间映射,而 flatMap() 从 Java 类型映射到另一个响应式流。 在代码清单 8.13 中,我们从 Order 类型的对象映射到 Mono
(由 OrderRepository 返回)。由于 map() 运算符期望目标类型不是响应式流,它仍然会将其包装在一个中并返回 Mono > 对象。另一方面,flatMap() 运算符期望目标类型是响应式流,因此它知道如何处理 OrderRepository 生成的发布者,并正确返回 Mono 对象。
在下一节中,你将通过暴露 API 来获取和提交订单,完成 Order Service 的基本实现。