单声道switchIfEmpty()总是被调用 [英] Mono switchIfEmpty() is always called

查看:85
本文介绍了单声道switchIfEmpty()总是被调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两种方法.
主要方法:

I have two methods.
Main method:

@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):

And this invoked method (the service calls an external 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);
                }
            });
} 

在上面的示例中,即使返回了 Mono.empty()的结果,也总是从main方法中调用 switchIfEmpty().

In the above example, switchIfEmpty() is always called from the main method, even when a result with Mono.empty() is returned.

我找不到解决这个简单问题的方法.
以下内容也不起作用:

I cannot find a solution for this simple problem.
The following also doesn't work:

Mono.just(null) 

因为该方法将抛出nullpointerexception.

Because the method will throw a nullpointerexception.

我也不能使用的是flatMap方法来检查 foundUser 是否为空.
不幸的是,万一我返回 Mono.empty(),flatMap根本不会被调用,所以我也不能在此处添加任何条件.

What I also can't use is the flatMap method to check that foundUser is null.
Sadly, flatMap doesn't get called at all in case I return Mono.empty(), so I cannot add a condition here either.

感谢您的帮助.

@ 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按值".这意味着,即使在您订阅单声道之前,此替代单声道的评估也已触发.

It's because switchIfEmpty accepts Mono "by value". Meaning that even before you subscribe to your mono, this alternative mono's evaluation is already triggered.

想象一个这样的方法:

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

It'll always trigger alternative no matter what during stream construction. To address this you can defer evaluation of a second mono by using Mono.defer

Mono<String> result = Mono.just("Some payload")
        .switchIfEmpty(Mono.defer(() -> asyncAlternative()));

这样,仅当需要替代项时,它才会打印"Hi there"(嗨在那里)

This way it will only print "Hi there" when alternative is requested

UPD:

详细说明我的答案.您面临的问题与Reactor无关,而与Java语言本身以及它如何解析方法参数有关.让我们检查一下我提供的第一个示例中的代码.

Elaborating a little on my answer. The problem you're facing is not related to Reactor but to Java language itself and how it resolves method parameters. Let's examine the code from the first example I provided.

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);

这两个代码段在语义上是等效的.我们可以继续对它们进行包装,以查看问题所在:

These two code snippets are semantically equivalent. We can continue unwrapping them to see where the problem lies:

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 类型时,将来的计算已经触发.为了防止不必要的计算,我们可以将未来包装在延后的评估中:

As you can see future computation was already triggered at the point when we start composing our Mono types. To prevent unwanted computations we can wrap our future into a defered evaluation:

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);

在第二个示例中,未来被困在一个懒惰的供应商中,并且仅在需要时才安排执行.

In second example the future is trapped in a lazy supplier and is scheduled for execution only when it will be requested.

这篇关于单声道switchIfEmpty()总是被调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆