总是调用 Mono switchIfEmpty()
Mono switchIfEmpty() is always called
我有两个方法。
主要方法:
@PostMapping("/login")
public Mono<ResponseEntity<ApiResponseLogin>> loginUser(@RequestBody final LoginUser loginUser) {
return socialService.verifyAccount(loginUser)
.flatMap(socialAccountIsValid -> {
if (socialAccountIsValid) {
return this.userService.getUserByEmail(loginUser.getEmail())
.switchIfEmpty(insertUser(loginUser))
.flatMap(foundUser -> updateUser(loginUser, foundUser))
.map(savedUser -> {
String jwts = jwt.createJwts(savedUser.get_id(), savedUser.getFirstName(), "user");
return new ResponseEntity<>(HttpStatus.OK);
});
} else {
return Mono.just(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
}
});
}
以及这个调用的方法(服务调用外部api):
public Mono<User> getUserByEmail(String email) {
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(USER_API_BASE_URI)
.queryParam("email", email);
return this.webClient.get()
.uri(builder.toUriString())
.exchange()
.flatMap(resp -> {
if (Integer.valueOf(404).equals(resp.statusCode().value())) {
return Mono.empty();
} else {
return resp.bodyToMono(User.class);
}
});
}
在上面的示例中,switchIfEmpty()
总是从主方法调用,即使 Mono.empty()
的结果是 returned。
我找不到这个简单问题的解决方案。
以下也不起作用:
Mono.just(null)
因为该方法会抛出一个NullPointerException
.
我也不能使用 flatMap 方法来检查 foundUser
是否为 null。
可悲的是,如果我 return Mono.empty()
,flatMap 根本不会被调用,所以我也不能在这里添加条件。
@SimY4
@PostMapping("/login")
public Mono<ResponseEntity<ApiResponseLogin>> loginUser(@RequestBody final LoginUser loginUser) {
userExists = false;
return socialService.verifyAccount(loginUser)
.flatMap(socialAccountIsValid -> {
if (socialAccountIsValid) {
return this.userService.getUserByEmail(loginUser.getEmail())
.flatMap(foundUser -> {
return updateUser(loginUser, foundUser);
})
.switchIfEmpty(Mono.defer(() -> insertUser(loginUser)))
.map(savedUser -> {
String jwts = jwt.createJwts(savedUser.get_id(), savedUser.getFirstName(), "user");
return new ResponseEntity<>(HttpStatus.OK);
});
} else {
return Mono.just(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
}
});
}
这是因为 switchIfEmpty 接受 Mono "by value"。这意味着即使在您订阅您的单声道之前,这个替代单声道的评估已经被触发。
想象这样一个方法:
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()));
这样它只会在请求替代时打印 "Hi there"
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);
在第二个例子中,future 被困在一个懒惰的供应商中,并且仅在被请求时才被安排执行。
对于那些尽管投票结果很好,但仍然不明白为什么会出现这种行为的人:
反应堆来源(Mono.xxx & Flux.xxx)是:
Lazily evaluated : 源内容evaluated/triggered 只有当订阅者订阅它时才;
或急切评估:甚至在订阅者订阅之前就立即评估来源的内容。
像Mono.just(xxx)
、Flux.just(xxx)
、Flux.fromIterable(x,y,z)
这样的表达是渴望的。
通过使用 defer()
,您可以强制对源进行惰性求值。这就是接受的答案有效的原因。
这样做:
someMethodReturningAMono()
.switchIfEmpty(buildError());
with buildError()
依赖急切的来源来创建替代的 Mono 将始终 在订阅之前进行评估:
Mono<String> buildError(){
return Mono.just("An error occured!"); //<-- evaluated as soon as read
}
要防止这种情况,请执行以下操作:
someMethodReturningAMono()
.switchIfEmpty(Mono.defer(() -> buildError()));
阅读此 了解更多信息。
我有两个方法。
主要方法:
@PostMapping("/login")
public Mono<ResponseEntity<ApiResponseLogin>> loginUser(@RequestBody final LoginUser loginUser) {
return socialService.verifyAccount(loginUser)
.flatMap(socialAccountIsValid -> {
if (socialAccountIsValid) {
return this.userService.getUserByEmail(loginUser.getEmail())
.switchIfEmpty(insertUser(loginUser))
.flatMap(foundUser -> updateUser(loginUser, foundUser))
.map(savedUser -> {
String jwts = jwt.createJwts(savedUser.get_id(), savedUser.getFirstName(), "user");
return new ResponseEntity<>(HttpStatus.OK);
});
} else {
return Mono.just(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
}
});
}
以及这个调用的方法(服务调用外部api):
public Mono<User> getUserByEmail(String email) {
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(USER_API_BASE_URI)
.queryParam("email", email);
return this.webClient.get()
.uri(builder.toUriString())
.exchange()
.flatMap(resp -> {
if (Integer.valueOf(404).equals(resp.statusCode().value())) {
return Mono.empty();
} else {
return resp.bodyToMono(User.class);
}
});
}
在上面的示例中,switchIfEmpty()
总是从主方法调用,即使 Mono.empty()
的结果是 returned。
我找不到这个简单问题的解决方案。
以下也不起作用:
Mono.just(null)
因为该方法会抛出一个NullPointerException
.
我也不能使用 flatMap 方法来检查 foundUser
是否为 null。
可悲的是,如果我 return Mono.empty()
,flatMap 根本不会被调用,所以我也不能在这里添加条件。
@SimY4
@PostMapping("/login")
public Mono<ResponseEntity<ApiResponseLogin>> loginUser(@RequestBody final LoginUser loginUser) {
userExists = false;
return socialService.verifyAccount(loginUser)
.flatMap(socialAccountIsValid -> {
if (socialAccountIsValid) {
return this.userService.getUserByEmail(loginUser.getEmail())
.flatMap(foundUser -> {
return updateUser(loginUser, foundUser);
})
.switchIfEmpty(Mono.defer(() -> insertUser(loginUser)))
.map(savedUser -> {
String jwts = jwt.createJwts(savedUser.get_id(), savedUser.getFirstName(), "user");
return new ResponseEntity<>(HttpStatus.OK);
});
} else {
return Mono.just(new ResponseEntity<>(HttpStatus.UNAUTHORIZED));
}
});
}
这是因为 switchIfEmpty 接受 Mono "by value"。这意味着即使在您订阅您的单声道之前,这个替代单声道的评估已经被触发。
想象这样一个方法:
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()));
这样它只会在请求替代时打印 "Hi there"
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);
在第二个例子中,future 被困在一个懒惰的供应商中,并且仅在被请求时才被安排执行。
对于那些尽管投票结果很好,但仍然不明白为什么会出现这种行为的人:
反应堆来源(Mono.xxx & Flux.xxx)是:
Lazily evaluated : 源内容evaluated/triggered 只有当订阅者订阅它时才;
或急切评估:甚至在订阅者订阅之前就立即评估来源的内容。
像Mono.just(xxx)
、Flux.just(xxx)
、Flux.fromIterable(x,y,z)
这样的表达是渴望的。
通过使用 defer()
,您可以强制对源进行惰性求值。这就是接受的答案有效的原因。
这样做:
someMethodReturningAMono()
.switchIfEmpty(buildError());
with buildError()
依赖急切的来源来创建替代的 Mono 将始终 在订阅之前进行评估:
Mono<String> buildError(){
return Mono.just("An error occured!"); //<-- evaluated as soon as read
}
要防止这种情况,请执行以下操作:
someMethodReturningAMono()
.switchIfEmpty(Mono.defer(() -> buildError()));
阅读此