Это потому, что switchIfEmpty принимает Mono «по значению». Это означает, что еще до того, как вы подпишетесь на свой моно, оценка этого альтернативного моно уже запущена.
Представьте себе метод, подобный этому:
Mono<String> asyncAlternative() {
return Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
System.out.println("Hi there");
return "Alternative";
}));
}
Если вы определите свой код следующим образом:
Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());
Это всегда вызовет альтернативу, независимо от того, что происходит во время создания потока Для решения этой проблемы вы можете отложить оценку второго моно с помощью Mono.defer
Mono<String> result = Mono.just("Some payload")
.switchIfEmpty(Mono.defer(() -> asyncAlternative()));
Таким образом, он будет печатать «Привет!» Только при запросе альтернативы
UPD:
Немного уточняю мой ответ. Проблема, с которой вы сталкиваетесь, связана не с Reactor, а с самим языком Java и с тем, как он разрешает параметры метода. Давайте рассмотрим код из первого предоставленного мною примера.
Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());
Мы можем переписать это в:
Mono<String> firstMono = Mono.just("Some payload");
Mono<String> alternativeMono = asyncAlternative();
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);
Эти два фрагмента кода семантически эквивалентны. Мы можем продолжить их развертывание, чтобы увидеть, в чем проблема:
Mono<String> firstMono = Mono.just("Some payload");
CompletableFuture<String> alternativePromise = CompletableFuture.supplyAsync(() -> {
System.out.println("Hi there");
return "Alternative";
}); // future computation already tiggered
Mono<String> alternativeMono = Mono.fromFuture(alternativePromise);
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);
Как вы можете видеть, будущие вычисления уже были запущены в тот момент, когда мы начали составлять наши Mono
типы. Чтобы предотвратить нежелательные вычисления, мы можем заключить наше будущее в отложенную оценку:
Mono<String> result = Mono.just("Some payload")
.switchIfEmpty(Mono.defer(() -> asyncAlternative()));
Который развернется в
Mono<String> firstMono = Mono.just("Some payload");
Mono<String> alternativeMono = Mono.defer(() -> Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
System.out.println("Hi there");
return "Alternative";
}))); // future computation defered
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);
Во втором примере будущее находится в ловушке у ленивого поставщика и запланировано к исполнению только тогда, когда оно будет запрошено.