1. 引言
Mono 是响应式编程中的核心概念,尤其在 Project Reactor 中。它代表一个最多发射一个元素后成功完成或出错的流,常用于返回单个结果或无结果的异步操作。
Mono 特别适合表示异步计算结果,比如数据库查询、HTTP 请求或返回单个值的操作。本文将深入探讨创建 Mono 的三种常见方式:just()
、defer()
和 create()
的区别。
2. Mono.just()
Mono.just()
是创建 Mono 的最简单方式。它接收一个现有值并包装成 Mono,使订阅者立即可用。当数据已就绪且无需复杂计算或延迟执行时,此方法最实用。
⚠️ 关键点:Mono.just()
会在 Mono 创建时立即计算并存储值,而非等到订阅时。一旦创建,即使底层数据源在订阅前被修改,值也不会改变。
@Test
void whenUsingMonoJust_thenValueIsCreatedEagerly() {
String[] value = {"Hello"};
Mono<String> mono = Mono.just(value[0]);
value[0] = "world";
mono.subscribe(actualValue -> assertEquals("Hello", actualValue));
}
此例中,Mono.just()
在创建时便急切地生成了值。尽管数组元素在订阅前被修改为 "world"
,但 Mono 仍发射原始值 "Hello"
。
✅ 适用场景:
- 已知的静态值可直接发射
- 不涉及计算或副作用的简单场景
3. Mono.defer()
Mono.defer()
实现了延迟执行——Mono 直到订阅时才会创建。这在需要避免不必要的资源分配或计算时特别有用。
@Test
void whenUsingMonoDefer_thenValueIsCreatedLazily() {
String[] value = {"Hello"};
Mono<String> mono = Mono.defer(() -> Mono.just(value[0]));
value[0] = "World";
mono.subscribe(actualValue -> assertEquals("World", actualValue));
}
此例中,数组值在 Mono 创建后被修改。由于 Mono.defer()
是延迟创建的,它不会捕获创建时的值,而是等到订阅时才获取。因此订阅时收到的是更新后的值 "World"
,而非原始值 "Hello"
。
💡 核心优势:defer()
能为每个订阅者创建独立的 Mono 实例,确保值反映订阅时的最新状态。当值需根据动态条件生成时,这个特性至关重要。
public Mono<String> getGreetingMono(String name) {
return Mono.defer(() -> {
String greeting = "Hello, " + name;
return Mono.just(greeting);
});
}
@Test
void givenNameIsAlice_whenMonoSubscribed_thenShouldReturnGreetingForAlice() {
Mono<String> mono = generator.getGreetingMono("Alice");
StepVerifier.create(mono)
.expectNext("Hello, Alice")
.verifyComplete();
}
@Test
void givenNameIsBob_whenMonoSubscribed_thenShouldReturnGreetingForBob() {
Mono<String> mono = generator.getGreetingMono("Bob");
StepVerifier.create(mono)
.expectNext("Hello, Bob")
.verifyComplete();
}
✅ 适用场景:
- 涉及数据库查询/网络调用等昂贵操作时,避免不必要执行
- 需动态生成值或计算依赖外部因素的场景
4. Mono.create()
Mono.create()
提供最大灵活性,允许完全控制发射过程。它通过 MonoSink
实现值、信号和错误的程序化生成。
⚡ 核心机制:
sink.success(value)
发射值sink.error(error)
发射错误sink.success()
无参调用表示完成
public Mono<String> performOperation(boolean success) {
return Mono.create(sink -> {
if (success) {
sink.success("Operation Success");
} else {
sink.error(new RuntimeException("Operation Failed"));
}
});
}
@Test
void givenSuccessScenario_whenMonoSubscribed_thenShouldReturnSuccessValue() {
Mono<String> mono = generator.performOperation(true);
StepVerifier.create(mono)
.expectNext("Operation Success")
.verifyComplete();
}
@Test
void givenErrorScenario_whenMonoSubscribed_thenShouldReturnError() {
Mono<String> mono = generator.performOperation(false);
StepVerifier.create(mono)
.expectErrorMatches(throwable -> throwable instanceof RuntimeException
&& throwable.getMessage()
.equals("Operation Failed"))
.verify();
}
✅ 适用场景:
- 需实现复杂发射逻辑/错误处理/背压管理
- 集成遗留系统或需精细控制的异步任务
5. 核心差异对比
特性 | Mono.just() |
Mono.defer() |
Mono.create() |
---|---|---|---|
执行时机 | 急切(创建时) | 延迟(订阅时) | 延迟(手动发射) |
值状态 | 静态/预定义 | 每次订阅动态生成 | 手动控制生成 |
典型场景 | 数据已就绪且不变 | 按需生成数据 | 复杂逻辑/外部系统集成 |
错误处理 | ❌ 无内置处理 | ✅ 创建阶段可处理错误 | ✅ 显式控制成功/错误 |
性能特点 | 静态值效率最高 | 动态/昂贵操作友好 | 复杂异步代码适用 |
6. 总结
本文深入分析了创建 Mono 的三种方法及其适用场景。理解 Mono.just()
、Mono.defer()
和 Mono.create()
的差异是高效使用 Java 响应式编程的关键。根据具体需求选择合适方法,能让代码更高效、可维护且贴合业务场景。
原文作者:Baeldung 技术团队
联系方式:contact@baeldung.com
原文链接:Mono just() vs defer() vs create() in Reactive Programming