首页 > 解决方案 > 获取@RestControllerAdvice 中带注释的@Async 方法中引发的异常

问题描述

这里有一个非常相似的问题,但答案不足以解决我的问题。

我在一个@Service类中有这个方法:

@Async
public void activateUser(...){
  if(someCondition){
   throw new GeneralSecurityException();
  }
}

控制器:

@GetMapping( "/activate")
public ResponseEntity<Void> activate(...){
    myService.activateUser(...);
}

控制器建议:

@RestControllerAdvice( basePackages = "com.app.security" )
public class SecurityAdviceController extends ResponseEntityExceptionHandler {

     @ExceptionHandler( GeneralSecurityException.class )
     public ResponseEntity<GeneralSecurityExceptionBody> handleGeneralSecurityException( GeneralSecurityException ex ) {
     return ResponseEntity
            .status( HttpStatus.MOVED_PERMANENTLY )
            .header( HttpHeaders.LOCATION, ex.getUrl() )
            .body( null );
}

我们到了。由于异常将在另一个线程中引发,我该如何继续使其可用于@RestControllerAdvice

许多人建议实施AsyncUncaughtExceptionHandler,我同意,但这并不能回答问题。

当我删除@Async时,一切都很好,我可以看到同一个线程执行所有任务,但@Async我有 2 个线程参与。

一种解决方案是让父线程抛出异常(但这太麻烦了,我不知道如何实现)。

谢谢你的帮助。

标签: javaspringspring-bootspring-mvcspring-async

解决方案


如果你真的想异步工作,那么很可能你使用了错误的工具——最好切换到Spring WebFlux并使用响应式方法。

回到这个问题,我可以提出两种方法:

  • 去掉@Async或者使用SyncTaskExecutor,所以任务会在调用线程中同步执行。
  • 摆脱@ExceptionHandler(GeneralSecurityException.class)这种特殊的方法。相反,使用CompletableFuture并提供 exceptionally经过处理的逻辑。下面是CompletableFuture在控制器和服务中使用的草图:
@Controller
public class ApiController {
    private final Service service;
    public ApiController(Service service) {
        this.service = service;
    }
    @GetMapping( "/activate")
    public CompletableFuture<Void> activate(...){
        return service.activateUser(...)
               .exceptionally(throwable -> ... exception handling goes here ...)
    }
}

@Service
public class Service {
    @Async
    public CompletableFuture<Void> activateUser(...) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        ... your code goes here ...
        if(someCondition){
           future.completeExceptionally(new GeneralSecurityException());
        } else {
           future.complete(null);
        }
        return future;
    }
}


推荐阅读