
在 Reactor 中,Mono 完成(onComplete)不等于“业务逻辑已执行”,因为过滤后为空的流也会静默完成。需借助 onSuccess 在 loadSmth 内部触发回调,而非在外部链上用 doOnSuccess —— 后者对空流同样触发。
在 reactor 中,`mono
在响应式编程中,一个常见误区是认为 doOnSuccess 或 doOnComplete 能准确反映“下游副作用是否发生”。但对 Mono 而言,空流(即被 filter 拦截后未进入 flatMap 的情况)会直接以 onComplete 终止,且 doOnSuccess 仍会被调用——因为 Mono 的成功语义是“正常终止”,而非“有值发出”或“逻辑已执行”。
关键在于:doOnSuccess 监听的是 Mono 的成功终止信号,而 Mono 的成功终止既可能来自 service.loadSmth() 的实际完成,也可能来自上游被过滤后的“空完成”。因此,将副作用逻辑(如 serviceContinueWork.doSmthElse())放在链末端是不可靠的。
✅ 正确做法:将副作用绑定到 loadSmth(msg) 的内部完成时机,而非整个链的完成时机。使用 .onSuccess(Runnable) 是最简洁、语义最清晰的方案:
public Mono<void> execute(String resource) {
return Mono.just(resource)
.filter(Objects::nonNull)
.filter(msg -> msg.startsWith("http://"))
.flatMap(msg -> service.loadSmth(msg)
.onSuccess(v -> serviceContinueWork.doSmthElse())) // ✅ 仅当 loadSmth 实际执行并完成时触发
.doOnError(t -> log.error("service#loadSmth error: {}", ExceptionUtils.getStackTrace(t)))
.then();
}</void>
这里 onSuccess(Runnable) 是 Mono 的实例方法(注意不是 doOnSuccess),它仅在该 Mono 发出 onNext(null) 并随后 onComplete() 时执行——而这只发生在 service.loadSmth(msg) 真正被调用且成功完成时。若 filter 拦截了输入,flatMap 分支根本不会执行,onSuccess 自然也不会触发。
⚠️ 注意事项:
- 不要混淆
onSuccess(Runnable)(属于Mono实例,有严格触发条件)与doOnSuccess(Consumer)(属于操作符,对任何成功终止都响应); -
onSuccess的Runnable参数在Mono场景下接收的是null(因无实际值),但其执行本身即代表“目标异步操作已成功落地”; - 若
loadSmth可能返回Mono.empty()(而非直接取消或错误),需额外确认其契约:理想情况下,loadSmth应保证非空完成(即要么成功执行并onComplete(),要么报错),避免语义歧义; - 此方案无需修改
service.loadSmth的签名(保持Mono),完全符合“不为可观测性牺牲接口设计”的原则。
总结:判断 Mono 是否“真正被调用”,本质是将观测点下沉至目标操作内部。利用 onSuccess 将副作用锚定在具体 Mono 的成功完成事件上,是 Reactor 中轻量、精准且符合函数式风格的标准实践。









