首页 > 解决方案 > 如何解决在 Spring Webflux 中将一个数据放入单声道到另一个单声道的问题?

问题描述

我想通过 webclient 对象从另一台服务器获取字符串数据,并将其放入另一个 Mono 对象。但在 webclient 中,只能在.subscribe().

因为responseBody.subscribe()方法是异步的,所以方法test()将在执行前返回resultmessage字段的对象responseBody.subscribe()

当然,我知道如果我返回responseBody对象而不是result对象,是没有问题的。但我想返回的不是responseBody对象,而是result具有非空字段的对象message

我想在responseBody'ssubscribe()完成后返回结果。如何更改我的代码?请帮我。


public Mono<ResultVO> test() {
    Mono<ResultVO> result = Mono.just(new ResultVO());

    WebClient client = webClientBuilder.baseUrl("http://XXXXXX").build();
    Mono<String> responseBody = client.get().uri("/aaaa/bbbbb").retrieve().bodyToMono(String.class);

    responseBody.subscribe( s -> {
        result.subscribe(g -> g.setMessage(s));
    });
    return result;
}
...

@Data
public class ResultVO {
    private long timestamp;
    private String ip;
    private String message;
    ...
}

我期待这样

{
    "timestamp": 1566662695203,
    "ip": "192.168.1.1",
    "message": "c0db76f6-4eb5-4f84-be8d-018d53b453bb"
}

但结果数据是,

{
    "timestamp": 1566662695203,
    "ip": "192.168.1.1",
    "message": ""
}

标签: javaspring-webfluxproject-reactor

解决方案


不建议将这种逻辑放入 subscribe 方法中,这很容易导致“回调地狱”并最终导致代码无法维护。此外,我没有看到共享test方法的调用者,但很可能其中一个 Monos 被订阅了两次,这也导致了相当混乱的行为。

相反,要组合 Monos,您可以使用 zip、zipWith、flatMap 和其他几个运算符。

一种使用 zipWith 方法的解决方案:

public Mono<ResultVO> test()
{
    WebClient client = WebClient.builder().baseUrl("http://XXXXXX").build();

    // dummy representation of another data source (db query, web service call...) 
    Mono<ResultVO> result = Mono.just(new ResultVO());

    Mono<String> responseBody = client.get().uri("/aaaa/bbbbb").retrieve().bodyToMono(String.class);

    return result.zipWith(responseBody,
            (resultObj, body) -> new ResultVO(resultObj.getTimestamp(), resultObj.getIp(), body));
}

其他几点注意事项:

  • 如果您通过响应式 WebFlux 应用程序的 REST 端点返回 JSON,那么您永远不需要手动订阅,Spring 会为您完成
  • 避免使用可变对象(使用 setter 创建后修改的对象),而是创建新对象,这将使您的代码更容易推理并且不易出现并发问题
  • 有用的阅读可用的 Reactor 操作符

推荐阅读