#### 1. 什么是 Mono?
在响应式编程范式中,**Mono** 是 Project Reactor 提供的核心类型之一,用于表示 **0-1 个元素的异步序列**。它类似于 Java 8 的 `Optional`,但专为异步场景设计,支持丰富的操作符和背压机制。
**核心特性**:
- 非阻塞异步处理
- 支持响应式流规范(Reactive Streams)
- 提供链式操作符(类似 Stream API)
- 内置错误处理和生命周期管理
#### 2. 为什么需要 Mono?
传统的同步编程在处理高并发、I/O 密集型场景时存在瓶颈,而 Mono 提供了更高效的解决方案:
```java
// 传统同步方式(阻塞线程)
User user = userRepository.findById(123); // 阻塞直到结果返回
// 响应式方式(非阻塞)
Mono<User> userMono = userRepository.findById(123); // 立即返回 Mono,结果异步到达
```
#### 3. Mono 的生命周期
Mono 的执行分为三个关键阶段:
1. **创建**:定义数据源(如 `Mono.just("data")`)
2. **组合**:通过操作符转换数据流(如 `map()`、`flatMap()`)
3. **订阅**:触发执行并处理结果(如 `subscribe()`)
**关键点**:Mono 是惰性的,只有在调用 `subscribe()` 或终端操作(如 `block()`)时才会真正执行。
#### 4. 基础操作:创建与订阅
##### 4.1 创建 Mono
```java
import reactor.core.publisher.Mono;
import java.util.concurrent.CompletableFuture;
public class MonoCreation {
public static void main(String[] args) {
// 1. 创建包含值的 Mono
Mono<String> monoWithValue = Mono.just("Hello Mono");
// 2. 创建空 Mono
Mono<Void> emptyMono = Mono.empty();
// 3. 从 Supplier 创建(懒加载)
Mono<String> supplierMono = Mono.fromSupplier(() -> "Generated Value");
// 4. 从 CompletableFuture 创建
CompletableFuture<String> future = CompletableFuture.completedFuture("Async Value");
Mono<String> futureMono = Mono.fromFuture(future);
// 5. 创建失败的 Mono
Mono<String> errorMono = Mono.error(new RuntimeException("Oops!"));
}
}
```
##### 4.2 订阅与消费
```java
// 基础订阅(处理值、错误和完成信号)
monoWithValue.subscribe(
value -> System.out.println("接收到值: " + value),
error -> System.err.println("发生错误: " + error.getMessage()),
() -> System.out.println("处理完成")
);
// 使用 doOnNext() 添加副作用(不影响数据流)
monoWithValue
.doOnNext(value -> System.out.println("预处理: " + value))
.subscribe();
// 阻塞获取结果(仅用于测试,生产环境避免使用)
String result = monoWithValue.block();
```
#### 5. 中级操作:转换与组合
##### 5.1 转换操作符
```java
// map() - 同步转换值
Mono<Integer> lengthMono = monoWithValue.map(String::length);
// flatMap() - 异步转换为另一个 Mono
Mono<String> upperCaseMono = monoWithValue
.flatMap(value -> Mono.just(value.toUpperCase()));
// defaultIfEmpty() - 为空时提供默认值
Mono<String> nonEmptyMono = Mono.empty()
.defaultIfEmpty("Default Value");
```
##### 5.2 组合操作符
```java
// then() - 按顺序执行多个 Mono
Mono<Void> sequence = Mono.just("Step 1")
.then(Mono.just("Step 2"))
.then();
// zip() - 合并多个 Mono 的结果
Mono<String> combinedMono = Mono.zip(
Mono.just("Hello"),
Mono.just("World"),
(a, b) -> a + " " + b
);
// concatWith() - 连接两个 Mono
Mono<String> concatenated = Mono.just("First")
.concatWith(Mono.just("Second"));
```
#### 6. 高级操作:错误处理与调度
##### 6.1 错误处理
```java
// onErrorReturn() - 出错时返回默认值
Mono<String> fallbackMono = errorMono
.onErrorReturn("Fallback Value");
// onErrorResume() - 出错时切换到备用逻辑
Mono<String> recoveredMono = errorMono
.onErrorResume(e -> {
System.err.println("Recovering from: " + e);
return Mono.just("Recovered Value");
});
// retry() - 重试机制
Mono<String> retriedMono = errorMono
.retry(3); // 最多重试3次
```
##### 6.2 线程调度
```java
import reactor.core.scheduler.Schedulers;
// subscribeOn() - 指定订阅发生的线程
Mono<String> ioBoundMono = Mono.fromCallable(() -> {
// 模拟IO操作
Thread.sleep(1000);
return "IO Result";
})
.subscribeOn(Schedulers.boundedElastic()); // 使用IO优化的线程池
// publishOn() - 指定后续操作符执行的线程
Mono<String> computationMono = ioBoundMono
.map(result -> result.toUpperCase())
.publishOn(Schedulers.parallel()); // 使用计算优化的线程池
```
#### 7. 实战场景:WebFlux 与数据库操作
##### 7.1 WebFlux 控制器示例
```java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable Long id) {
return userService.findById(id)
.switchIfEmpty(Mono.error(new UserNotFoundException("User not found")));
}
}
```
##### 7.2 R2DBC 响应式数据库操作
```java
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import reactor.core.publisher.Mono;
public interface UserRepository extends R2dbcRepository<User, Long> {
// 自动生成响应式查询方法
Mono<User> findById(Long id);
}
// 服务层示例
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public Mono<User> findById(Long id) {
return userRepository.findById(id)
.doOnNext(user -> log.info("Found user: {}", user.getName()));
}
}
```
#### 8. 最佳实践与注意事项
1. **避免阻塞操作**:尽量使用 `flatMap()` 而非 `block()`
2. **资源管理**:使用 `doFinally()` 确保资源释放
```java
Mono<Connection> connectionMono = Mono.using(
() -> createConnection(), // 资源创建
conn -> processData(conn), // 资源使用
conn -> closeConnection(conn) // 资源释放
);
```
3. **调试技巧**:使用 `log()` 操作符记录数据流状态
```java
userMono
.log("user-fetch")
.subscribe();
```
4. **测试建议**:使用 StepVerifier 验证 Mono 行为
```java
import reactor.test.StepVerifier;
@Test
public void testUserNotFound() {
StepVerifier.create(userService.findById(-1L))
.expectError(UserNotFoundException.class)
.verify();
}
```
#### 9. 常见问题解答
**Q1:Mono 和 CompletableFuture 有什么区别?**
A:Mono 是响应式的,支持背压和丰富的操作符链;CompletableFuture 是基于回调的异步编程模型,缺乏操作符组合能力。
**Q2:何时使用 Mono,何时使用 Flux?**
A:Mono 用于 0-1 个元素的场景(如单个查询结果),Flux 用于多个元素的序列(如集合查询)。
**Q3:Mono 是线程安全的吗?**
A:是的,Mono 本身是不可变的,其操作符也是线程安全的,但要注意处理共享可变状态时的线程安全问题。
#### 总结
Mono 是响应式编程的基础构建块,适用于现代高并发、低延迟的应用场景。通过掌握 Mono 的创建、转换、组合和错误处理,你可以构建出高效且健壮的异步系统。建议结合 Spring WebFlux 和 R2DBC 进行实践,以充分发挥响应式编程的威力。