带你了解 Java 中的 Mono 接口 🚀
在 Java 响应式编程(Reactive Programming)中,Mono 是 Project Reactor 库的核心类型之一。它不是 Java 标准库(JDK)中的接口,而是 Reactor Core 提供的响应式类型,常与 Spring WebFlux、R2DBC 等响应式框架一起使用。
Mono 的设计灵感来源于 Reactive Streams 规范,专门用于处理 0 或 1 个元素 的异步数据流。
1. Mono 是什么?(核心定义)
- Mono 是一个 Publisher(Reactive Streams 中的发布者接口)。
- 它代表一个异步序列,该序列最多发出一个元素(onNext),然后以 onComplete(完成)或 onError(错误)信号终止。
- 可以发出:
- 0 个元素(空结果,直接完成)
- 1 个元素(单个值)
- 永远不会发出多个元素(与 Flux 区别)
一句话比喻:
- Mono ≈ Java 中的 Optional + CompletableFuture(异步 + 0/1 值)
- Flux ≈ Java 中的 Stream 或 List(0 到 N 个元素)
官方文档定义(Reactor 3.x):
A Reactive Streams Publisher with basic rx operators that emits at most one item via the onNext signal then terminates with the onComplete signal.
2. Mono 与 Flux 的区别(面试高频)
| 维度 | Mono | Flux |
|---|---|---|
| 元素数量 | 0 或 1 | 0 到 N(可无限) |
| 适用场景 | 单个结果(如查询一条记录、HTTP 返回单个对象) | 多个结果(如查询列表、实时数据流) |
| 类比 Java 类型 | Optional / CompletableFuture | Stream / List / Collection |
| 常用操作符 | map、flatMap、then、zipWith、switchIfEmpty | map、flatMap、filter、take、merge、zip |
| 返回类型 | 通常返回 Mono | 通常返回 Flux |
| 背压支持 | 支持(Reactive Streams 标准) | 支持 |
什么时候用 Mono?
- 数据库单条查询(findById)
- HTTP 接口返回单个对象
- 异步计算单个结果(如远程调用、文件读取单值)
- 组合多个异步操作的最终结果
3. Mono 的创建方式(常用工厂方法)
import reactor.core.publisher.Mono;
import java.time.Duration;
// 1. 创建有值的 Mono
Mono<String> mono1 = Mono.just("Hello, Reactor!");
// 2. 创建空 Mono
Mono<String> monoEmpty = Mono.empty();
// 3. 从 Supplier 创建(延迟执行)
Mono<String> monoSupplier = Mono.fromSupplier(() -> "延迟计算的值");
// 4. 从 Callable / Runnable 创建
Mono<String> monoCallable = Mono.fromCallable(() -> "从 Callable 返回");
// 5. 延迟创建(延迟指定时间)
Mono<String> delayed = Mono.delay(Duration.ofSeconds(2))
.map(tick -> "延迟2秒后返回");
// 6. 从 CompletableFuture 转换
Mono<String> fromFuture = Mono.fromFuture(CompletableFuture.supplyAsync(() -> "从 Future 来"));
// 7. 错误 Mono
Mono<Object> errorMono = Mono.error(new RuntimeException("出错了!"));
4. Mono 的核心操作符(Operators)
Mono 支持大量函数式操作符,实现声明式编程:
Mono<User> userMono = userRepository.findById(1L) // 返回 Mono<User>
.map(user -> user.getName().toUpperCase()) // 转换
.filter(name -> name.length() > 5) // 过滤(结果仍为 Mono)
.flatMap(name -> fetchDetail(name)) // 扁平化嵌套 Mono
.switchIfEmpty(Mono.just("默认用户")) // 为空时替换
.doOnSuccess(user -> System.out.println("成功: " + user))
.doOnError(err -> System.err.println("错误: " + err))
.subscribe(); // 触发执行
常用操作符分类:
- 转换:
map、flatMap - 组合:
zipWith、then、and - 条件:
switchIfEmpty、defaultIfEmpty - 副作用:
doOnNext、doOnError、doOnSubscribe - 终止:
then()(忽略值,返回新 Mono)
5. 订阅(Subscribe)与执行
Mono 是懒执行(Lazy)的,只有调用 subscribe() 才会真正开始执行:
Mono<String> mono = Mono.just("测试");
// 方式1:简单订阅
mono.subscribe(System.out::println);
// 方式2:完整处理 onNext / onError / onComplete
mono.subscribe(
value -> System.out.println("收到: " + value),
error -> System.err.println("错误: " + error),
() -> System.out.println("完成!")
);
在 Spring WebFlux 中,Controller 方法直接返回 Mono<T> 或 Flux<T>,框架会自动订阅。
6. 实际应用场景(Spring WebFlux 示例)
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public Mono<User> getUser(@PathVariable Long id) {
return userService.findById(id) // 返回 Mono<User>
.switchIfEmpty(Mono.error(new NotFoundException("用户不存在")));
}
@PostMapping
public Mono<User> createUser(@RequestBody User user) {
return userService.save(user);
}
}
7. 与其他异步类型的转换
Mono.fromFuture(CompletableFuture)Mono.fromCallable(...)Flux.fromIterable(list).single()→ 转 Mono(期望单个元素)mono.as(Flux::from)或flux.single()
8. 注意事项 & 最佳实践
- 不要阻塞:Mono 内部操作应保持非阻塞(避免
block(),除非测试)。 - 错误处理:优先使用
onErrorReturn、onErrorResume而不是 try-catch。 - 背压:Mono 天然支持背压(下游控制上游速率)。
- 线程模型:默认在当前线程,常用
subscribeOn/publishOn切换调度器(Schedulers)。 - 测试:用
StepVerifier(Reactor Test)测试 Mono/Flux。 - 性能:响应式编程适合高并发 IO 场景,不适合纯 CPU 密集型计算。
Mono vs CompletableFuture:
- Mono:更丰富的操作符 + 声明式 + 支持背压 + 可取消。
- CompletableFuture:更简单,但功能较少。
总结:
Mono 是 Reactor 中处理单个异步结果的利器,它让异步编程变得声明式、组合式、可读性强。掌握 Mono + Flux 后,你就能轻松驾驭 Spring WebFlux、响应式微服务等现代 Java 技术栈。
想继续深入吗?
回复以下任意内容,我立刻带你继续:
- “Mono 常用操作符 20 例详解 + 代码”
- “Flux 完全对比详解”
- “Spring WebFlux 中 Mono/Flux 实战项目”
- “Mono 错误处理与重试机制”
- “StepVerifier 测试 Mono/Flux”
- “Reactor vs RxJava 对比”
- “从 CompletableFuture 迁移到 Mono”
或者直接说:“给我一个完整的 Mono 入门小项目”。
有任何疑问,随时问我,我们一步步把响应式编程学透!💪