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


原始标题:Mono just() vs defer() vs create() in Reactive Programming | Baeldung