Spring Reactor:Mono.zip在空Mono上失败

ath*_*hom 5 java spring project-reactor

我正在使用Spring Reactor 3.1.0.M3并且有一个用例,我需要从多个来源合并Mono.我发现如果其中一个Monos是一个空Mono,zip会失败而不会出错.

例:

Mono<String> m1 = Mono.just("A");
Mono<String> m2 = Mono.just("B");
Mono<String> m3 = Mono.empty();

Mono<String> combined = Mono.zip(strings -> {
    StringBuffer sb = new StringBuffer();
    for (Object string : strings) {
        sb.append((String) string);
    }
    return sb.toString();
}, m1, m2, m3);
System.out.println("Combined " + combined.block());
Run Code Online (Sandbox Code Playgroud)

添加m3时,响应中的组合子被跳过为空.当我删除m3时,它都按预期工作,并返回"AB".有没有办法通过检测空Mono来处理这个问题?另外,有没有办法让组合器方法知道对象的类型而不必抛出?

Bri*_*zel 13

zip运算符的行为不是这样的.这实际上是违反直觉的:你的代码期望一个3元组的元组而你只有两个?!?

在这种情况下,您可以控制并且只有在没有提供的情况下才能确定什么是良好的默认值(请记住,null反应流规范禁止使用值).

Mono<String> m1 = Mono.just("A");
Mono<String> m2 = Mono.just("B");
Mono<String> m3 = Mono.empty().defaultIfEmpty("");

Mono<String> combined = Mono.when(m1, m2, m3).map(t -> {
    StringBuffer sb = new StringBuffer();
    sb.append(t.getT1());
    sb.append(t.getT2());
    sb.append(t.getT3());
    return sb.toString();
});
Run Code Online (Sandbox Code Playgroud)

编辑

您似乎对Publisher类型的性质感到困惑,请参阅:

如果其中一个Monos是一个空的Mono,zip会失败而不会出错

因此,如果我试图压缩Mono's并且由于某种原因,一个是空的,拉链将失败,我似乎无法提供任何代码来防止

Mono不是故障情况:只是没有发出任何值并且成功完成.您可以通过更改代码示例来验证:

    combined.subscribe(
            s -> System.out.println("element: " + s), // doesn't execute
            s -> System.out.println("error: " + s), // doesn't execute
            () -> { System.out.println("complete!"); // prints
    });
Run Code Online (Sandbox Code Playgroud)

因此,根据您的要求,您可以:

  • 如果有可以依赖的方便的默认值,则defaultIfEmpty在这3个Mono实例上应用运算符
  • defaultIfEmpty在组合上应用运算符Mono,使用默认值或甚至将其转换为错误消息combined.switchIfEmpty(Mono.error(...))


Mar*_*nyi 9

在这种情况下,String为空情况定义默认值非常容易,这可以很好地解决问题,如 Brian 的回答中所述。但是,对于其他自定义类型,由于某种原因可能很难创建空对象。这些情况的替代方法是使用Optional. 不过,这个解决方案有一些繁重的样板。

Mono<Optional<String>> m1 = Mono.just("A").map(Optional::of).defaultIfEmpty(Optional.empty());
Mono<Optional<String>> m2 = Mono.just("B").map(Optional::of).defaultIfEmpty(Optional.empty());
Mono<Optional<String>> m3 = Mono.<String>empty().map(Optional::of).defaultIfEmpty(Optional.empty());

Mono<String> combined = Mono.zip(strings -> {
    StringBuffer sb = new StringBuffer();
    for (Object string : strings) {
        ((Optional<String>) string).ifPresent(sb::append);
    }
    return sb.toString();
}, m1, m2, m3);
System.out.println("Combined " + combined.block());
Run Code Online (Sandbox Code Playgroud)