我正在尝试使用Spring Webflux和Spring MVC,并遇到了一个有趣的案例。

从一个简单的 Controller 开始:

@GetMapping
public Mono<String> list(final Model model) {
    Flux<User> users = this.userRepository.findAll();
    model.addAttribute("users", users);
    return Mono.just("users/list");
}
userReposutory是基于自定义ConcurrentHashMap的实现。在这里,您可以找到findAll方法:
@Override
public Flux<User> findAll() {
    return Flux.fromIterable(this.users.values());
}

每当我尝试返回访问“用户/列表” View 时,一切似乎都正常运行。

但是,如果我尝试使用惯用的 react 方法来重写 Controller ,则会开始出现问题:
@GetMapping
public Mono<String> list(final Model model) {
    return this.userRepository.findAll()
      .collectList()
      .doOnEach(users -> model.addAttribute("users", users.get()))
      .map(u -> "users/list");
}

如果我击中了端点,我会在日志中得到它:
java.lang.IllegalArgumentException: ConcurrentModel does not support null attribute value
    at org.springframework.util.Assert.notNull(Assert.java:193)
    at org.springframework.ui.ConcurrentModel.addAttribute(ConcurrentModel.java:75)
    at org.springframework.ui.ConcurrentModel.addAttribute(ConcurrentModel.java:39)
    at com.baeldung.lss.web.controller.UserController.lambda$list$0(UserController.java:37)
    at reactor.core.publisher.FluxDoOnEach$DoOnEachSubscriber.onError(FluxDoOnEach.java:132)

显然,有些杂乱的null正在其中。然后,让我们急切地过滤掉所有这些:
@RequestMapping
public Mono<String> list(final Model model) {
    return this.userRepository.findAll()
      .filter(Objects::nonNull)
      .collectList()
      .filter(Objects::nonNull)
      .doOnEach(users -> model.addAttribute("users", users.get()))
      .map(u -> "users/list");
}

同样的问题,但是...如果我压缩map()调用中的所有内容,则所有内容都会再次起作用:
@GetMapping
public Mono<String> list(final Model model) {
    return this.userRepository.findAll()
      .collectList()
      .map(users -> {
          model.addAttribute("users", users);
          return "users/list";
      });
}

虽然,将副作用放置在map中并不是最佳选择。

有什么想法,这里的doOnEach()有什么问题吗?

最佳答案

非常好的问题。让我们看看JavaDocs讲述了doOnEach的内容:



好奇的。 users中的doOnEach(users -> ...)不是List<User>,而是Signal<List<User>>。该Signal<T>对象不会为空,这说明了第二个版本中的filter方法为什么不起作用的原因。

JavaDocs for Signal<T> 表示get()方法已显式标记为@Nullable,并且仅在下一项到达时才返回非null值。如果生成了完成或错误信号,则它将返回null

解决方案:

  • 改用doOnNext:您对下一个值感兴趣,而不是对来自源流的任何信号感兴趣。
  • doOnEach lambda进行空检查:这也可以工作,但是由于您对其他事件不感兴趣,因此是多余的。
  • 10-06 03:56