我正在尝试使用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进行空检查:这也可以工作,但是由于您对其他事件不感兴趣,因此是多余的。