关于Reactor中Mono的doOnNext、doOnSuccess、doOnTerminate执行顺序与预期不符的疑问
嘿,我来帮你拆解这个问题,纠正一下你的理解误区!
首先,你之前认为doOnNext→doOnSuccess→doOnTerminate有固定的执行顺序,这是一个常见的误解。实际上,这些doOnXXX操作符的回调触发顺序完全由它们在链式调用中的位置决定——每个doOnXXX就像信号流中的一个“拦截器”,当对应信号(比如onNext、onComplete)从上游流向下游时,会依次经过每个操作符,触发该操作符的回调逻辑。
核心逻辑:信号流的方向与操作符的位置
Reactor的Publisher(比如Mono)遵循两个关键的流动方向:
- 订阅请求流:从下游(
subscribe())向上游(Mono.just)传递,用于建立订阅关系。 - 数据/终止信号流:从上游(
Mono.just)向下游(subscribe())传递,也就是我们关心的事件触发流。
你的测试代码链式顺序是:
mono.doOnSubscribe(s -> System.out.println("Just got subscribed") ) .doOnTerminate(() -> System.out.println("Termination happens at the end")) .doOnSuccess(s -> System.out.println("I am successfull " + s)) .doOnNext(s -> System.out.println("next next next" + s)) .subscribe(s -> System.out.println("Subscribe" + s));
我们一步步拆解执行流程:
1. 订阅建立阶段
当调用subscribe()时,订阅请求从下游往上游传递,最终触发Mono.just发出onSubscribe信号。这个信号向下流经doOnSubscribe操作符时,触发它的回调,所以第一个输出是:
Just got subscribed
2. 数据信号(onNext)流动阶段
Mono.just发出onNext("ABC")信号,从上游向下游依次经过每个操作符:
- 经过
doOnSubscribe:它只监听订阅事件,不处理onNext,直接传递信号。 - 经过
doOnTerminate:它只监听终止信号(onComplete/onError),不处理onNext,直接传递信号。 - 经过
doOnSuccess:它只监听成功终止信号(onComplete),不处理onNext,直接传递信号。 - 经过
doOnNext:监听onNext信号,触发回调,输出:next next nextABC - 到达
subscribe()的回调,输出:SubscribeABC
3. 终止信号(onComplete)流动阶段
Mono.just在发出onNext后,会立即发出onComplete终止信号,同样从上游向下游流动:
- 经过
doOnSubscribe:无处理,直接传递。 - 经过
doOnTerminate:监听终止信号,触发回调,输出:Termination happens at the end - 经过
doOnSuccess:监听成功终止信号,触发回调,输出:I am successfull ABC - 经过
doOnNext:不处理终止信号,直接传递到subscribe(),无额外输出。
所以正确的输出顺序应该是:
Just got subscribed next next nextABC SubscribeABC Termination happens at the end I am successfull ABC
如果你的输出和这个不一致,可能是代码书写时的顺序错误,或者Reactor版本的微小差异,但核心逻辑不变:doOnXXX的执行顺序完全取决于它们在链式调用中的位置,信号流经哪个操作符,就触发哪个操作符的对应回调。
比如,如果你调整链式顺序为doOnNext→doOnSuccess→doOnTerminate,那么终止信号会先经过doOnSuccess再到doOnTerminate,输出顺序就会变成doOnSuccess的回调先于doOnTerminate。
内容的提问来源于stack exchange,提问作者Ankit Ghildiyal

