ric*_*din 8 spring java-8 spring-boot completable-future
我正在使用Spring Boot 1.5,我有一个异步执行的控制器,返回一个CompletableFuture<User>
.
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private final UserService service;
@GetMapping("/{id}/address")
public CompletableFuture<Address> getAddress(@PathVariable String id) {
return service.findById(id).thenApply(User::getAddress);
}
}
Run Code Online (Sandbox Code Playgroud)
该方法UserService.findById
可以抛出一个UserNotFoundException
.所以,我开发了专门的控制器建议.
@ControllerAdvice(assignableTypes = UserController .class)
public class UserExceptionAdvice {
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public String handleUserNotFoundException(UserNotFoundException ex) {
return ex.getMessage();
}
}
Run Code Online (Sandbox Code Playgroud)
问题是,在向控制器发出未知用户请求的情况下,测试不会返回HTTP 500状态而不是404状态.
这是怎么回事?
ric*_*din 11
问题是由于CompletableFuture
在后续阶段如何完成异常处理异常.
正如javadoc中所述CompletableFuture
[..]如果一个阶段的计算突然以(未经检查的)异常或错误终止,那么所有需要完成的依赖阶段也会异常完成,并且CompletionException将异常作为其原因.[..]
在我的情况下,该thenApply
方法创建一个新的CompletionStage
包装与CompletionException
原始实例UserNotFoundException
:(
遗憾的是,控制器建议不执行任何解包操作.Zalando开发人员也发现了这个问题:Async CompletableFuture附加错误
因此,使用CompletableFuture
和控制器建议在Spring中实现异步控制器似乎不是一个好主意.
部分解决方案是将a重新映射CompletableFuture<T>
为a DeferredResult<T>
.在这篇博客中,给出了可能的适配器的实现.
public class DeferredResults {
private DeferredResults() {}
public static <T> DeferredResult<T> from(final CompletableFuture<T> future) {
final DeferredResult<T> deferred = new DeferredResult<>();
future.thenAccept(deferred::setResult);
future.exceptionally(ex -> {
if (ex instanceof CompletionException) {
deferred.setErrorResult(ex.getCause());
} else {
deferred.setErrorResult(ex);
}
return null;
});
return deferred;
}
}
Run Code Online (Sandbox Code Playgroud)
因此,我的原始控制器将更改为以下内容.
@GetMapping("/{id}/address")
public DeferredResult<Address> getAddress(@PathVariable String id) {
return DeferredResults.from(service.findById(id).thenApply(User::getAddress));
}
Run Code Online (Sandbox Code Playgroud)
我无法理解为什么Spring原生支持CompletableFuture
作为控制器的返回值,但它在控制器通知类中无法正确处理.
希望能帮助到你.
归档时间: |
|
查看次数: |
1999 次 |
最近记录: |