获取@RestControllerAdvice中带注释的@Async方法中引发的异常

时间:2019-02-23 23:38:26

标签: java spring spring-boot spring-mvc spring-async

有一个非常相似的问题here,但是答案不足以回答我的问题。

我在@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个线程。

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

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

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

回到问题,我可以建议2种方法:

  • 摆脱@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;
    }
}