如何链式调用两个返回Mono的Spring Mongo Reactive Save操作?
解决Reactor链式调用中Mono操作挂起/不触发的问题
这种情况我碰到过好几个小伙伴都踩过坑,核心问题基本都是没搞清楚Reactor响应式流的惰性执行和异步操作串联的正确姿势,咱们一步步来捋清楚:
先说说你可能踩的坑
你提到单独执行各操作都正常,但链式调用就挂起,大概率是下面这两个原因之一:
- 用了
map而不是flatMap处理异步操作:map是同步转换,如果你的createAndUpdateWithProfile或者convertAndSave返回的是Mono,用map会把它包装成Mono<Mono<Trait>>,内部的那个Mono根本不会被订阅执行,流自然就停住了。 - 没有正确串联操作,或者没触发订阅:Reactor的流是惰性的,只有当你调用
subscribe()(或者测试里用StepVerifier验证)时才会执行,如果你的链式调用最后没订阅,或者中间的操作没通过操作符串联,那后面的步骤就不会触发。
正确的链式写法示例
咱们按照你的需求,用flatMap来串联所有异步操作,确保前一步完成后再执行下一步:
// 按顺序执行:保存Profile → 创建更新Trait → 转换保存Trait profileService.save(yourProfile) // 第一步完成后,用flatMap处理下一个异步操作(创建并更新Trait) .flatMap(savedProfile -> { // 这里假设createAndUpdateWithProfile返回Mono<Trait>,如果是同步返回Trait可以用map,但异步必须用flatMap return traitService.createAndUpdateWithProfile(savedProfile); }) // 第二步完成后,处理转换并保存Trait,同样用flatMap处理异步保存 .flatMap(updatedTrait -> { return traitService.convertAndSave(updatedTrait); }) // 最后一定要订阅,触发整个流的执行 .subscribe( finalSavedTrait -> { // 处理最终保存后的Trait System.out.println("最终保存的Trait: " + finalSavedTrait); }, error -> { // 处理整个流程中的错误 error.printStackTrace(); } );
测试用例的正确写法(用StepVerifier)
如果是在测试里,一定要用StepVerifier来触发流的执行,而不是靠block()(block()会阻塞线程,还容易出问题):
@Test void testProfileAndTraitSaveFlow() { // 准备测试数据 Profile testProfile = new Profile("test-id", "test-name"); Profile savedProfile = new Profile("saved-id", "test-name"); Trait updatedTrait = new Trait("trait-id", savedProfile); Trait finalTrait = new Trait("trait-id", savedProfile, "converted-value"); // 模拟服务的返回 when(profileService.save(any(Profile.class))).thenReturn(Mono.just(savedProfile)); when(traitService.createAndUpdateWithProfile(any(Profile.class))).thenReturn(Mono.just(updatedTrait)); when(traitService.convertAndSave(any(Trait.class))).thenReturn(Mono.just(finalTrait)); // 构建整个响应式流 Mono<Trait> resultFlow = profileService.save(testProfile) .flatMap(traitService::createAndUpdateWithProfile) .flatMap(traitService::convertAndSave); // 用StepVerifier验证流的执行顺序和结果 StepVerifier.create(resultFlow) .expectNext(finalTrait) // 期望最终返回finalTrait .verifyComplete(); // 触发流执行并验证完成 }
几个关键提醒
- 永远记住惰性执行:响应式流不会自动执行,必须有订阅动作(
subscribe()或StepVerifier的verify方法)才会启动整个流程。 - 异步操作用flatMap:只要你的方法返回
Mono/Flux,就用flatMap(或者concatMap保证顺序,switchMap切换最新流),别用map。 - 别随便用block():在链式调用里用
block()会破坏响应式的非阻塞特性,还可能导致线程死锁,尤其是在测试或者WebFlux环境里。
内容的提问来源于stack exchange,提问作者KJQ
相关产品推荐
相关产品推荐

