带你了解Java中的Mono接口

带你了解 Java 中的 Mono 接口

首先要明确一点:

Java 标准库(JDK)中并没有一个叫 Mono 的接口

你现在看到的 “Mono” 几乎 100% 指的是 Project Reactor 项目中的核心类型 reactor.core.publisher.Mono<T>,它是目前 Java 响应式编程领域使用最广泛的实现之一(与 Flux 并列)。

所以当面试官、企业内部文档、Spring 社区文章提到 “Java 中的 Mono 接口” 时,99% 的情况下指的都是 Reactor Mono

下面用最清晰的结构带你完整理解它。

1. Mono 是什么?一句话定义

Mono 代表 “0 或 1 个元素”的异步 / 响应式序列

  • 0 个元素 → 完成(onComplete)或错误(onError)
  • 1 个元素 → 发出一个值后完成
  • 永远不会发出第二个元素(与 Flux 最大的区别)

它对应了 Reactive Streams 规范中的 Publisher,但语义上更接近:

  • Optional + Future + Publisher 的结合体
  • “将来可能有 0~1 个结果”的承诺

2. Mono vs Flux 对比(最常被问)

维度MonoFlux生活类比
元素个数0 或 10 ~ NMono = 快递(要么到要么没到)
Flux = 直播弹幕流
典型语义单个结果、HTTP response、数据库单条记录数据流、事件流、日志流、消息队列
常见操作符数量约 50+ 个约 150+ 个Flux 操作符更丰富
empty()Mono.empty()Flux.empty()
just()Mono.just(value)Flux.just(v1, v2, …)
error()Mono.error(e)Flux.error(e)
fromCallable支持支持
block()返回 T 或 null(空时)返回 Iterable阻塞订阅

一句话记忆口诀

  • Mono:要么给一个,要么啥都不给(单结果承诺)
  • Flux:源源不断给(多结果流)

3. Mono 最常用的创建方式(Top 10)

// 1. 直接给值
Mono.just("重阳")                    // Mono<String>

// 2. 空(成功但无数据)
Mono.empty()                         // Mono<Void> 或任何类型

// 3. 错误
Mono.error(new RuntimeException("出错了"))

// 4. 延迟创建(推荐)
Mono.fromCallable(() -> heavyComputation())     // 只有订阅时才执行
Mono.fromSupplier(() -> createExpensiveObject())
Mono.fromFuture(future)                         // 适配 CompletableFuture
Mono.fromCompletionStage(completionStage)

// 5. 延迟 + 调度器
Mono.delay(Duration.ofSeconds(3))
    .thenReturn("3秒后出现")

// 6. 从 Optional 转换(非常常用)
Optional<String> opt = ...;
Mono<String> mono = Mono.justOrEmpty(opt);

// 7. defer(每次订阅都重新创建)
Mono.defer(() -> Mono.just(UUID.randomUUID().toString()))

// 8. 忽略值,只关心完成
Mono.from(runnableMono).then()                  // → Mono<Void>

4. Mono 最核心、最常考的操作符(面试高频)

操作符作用典型使用场景
map / flatMap转换值 / 展开另一个 Mono数据映射、调用下游服务
switchIfEmpty如果为空则用备选 Mono默认值、降级
defaultIfEmpty如果为空则给默认值简单降级
filter过滤(不符合就 empty)条件筛选
doOnNext / doOnError / doOnSuccess副作用(日志、埋点)调试、监控
then / thenReturn忽略上游值,只关心完成顺序执行
zipWhen / zipWith与另一个 Mono 组合并发请求合并
cache缓存结果(后续订阅复用)昂贵操作结果复用
retry / retryWhen失败重试网络请求重试
timeout超时则错误防止请求挂起

经典组合写法示例(Spring WebFlux 常见模式)

userService.findById(id)
    .switchIfEmpty(Mono.error(new NotFoundException("用户不存在")))
    .flatMap(user -> orderService.findLatestOrder(user.getId()))
    .map(Order::getAmount)
    .defaultIfEmpty(BigDecimal.ZERO)
    .doOnNext(amount -> log.info("用户 {} 最近订单金额: {}", id, amount))
    .timeout(Duration.ofSeconds(5))
    .onErrorResume(e -> Mono.just(BigDecimal.ZERO));

5. Mono 在 Spring 生态中的真实定位(2026 视角)

场景返回类型通常是说明
Spring WebFlux ControllerMono / Flux响应式 Web 核心
Spring Data R2DBCMono / Flux响应式数据库客户端
WebClientMono / Mono响应式 HTTP 客户端
Spring SecurityMono响应式认证上下文
Spring Cloud FunctionMono函数式编程风格
Reactor Kafka / RabbitMQFlux / Mono响应式消息中间件

6. 面试最常被问的 8 个 Mono 问题(建议背熟)

  1. Mono 和 Flux 的本质区别是什么?
  2. Mono.empty() 和 Mono.just(null) 有什么不同?
  3. flatMap 和 map 在 Mono 上的行为差异?
  4. 如何让 Mono 在空时抛出自定义异常?
  5. Mono 和 CompletableFuture 怎么互相转换?
  6. block() 和 subscribe() 的区别和风险?
  7. 如何实现“先查缓存,没命中再查 DB,最后写回缓存”?
  8. timeout + retryWhen 的组合怎么写才合理?

一句话总结

Mono 是 Reactor 体系中“单个异步结果”的标准载体,它是 Java 响应式编程世界里最优雅、最常用、最具表达力的“单值 Promise”。

如果你当前是在准备 Spring WebFlux、响应式微服务、R2DBC、WebClient 等相关面试或项目,那么 Mono 几乎是绕不开的 daily 角色。

你现在最想继续深入 Mono 的哪个部分?

  • 手写 10 个最常见的 Mono 组合模式
  • Mono + WebClient 的生产级最佳实践
  • Mono 与 CompletableFuture / CompletionStage 的互转对比
  • Mono 在错误处理上的各种姿势(onErrorResume / onErrorReturn / onErrorMap / doOnError)
  • Mono vs Optional vs Future 的哲学对比

告诉我,我马上给你更细致的代码 + 原理说明。

文章已创建 4893

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部