获取在@RestControllerAdvice 中带注释的@Async 方法中抛出的异常
Get exception thrown within annotated @Async method in @RestControllerAdvice
有一个非常相似的问题,但答案不足以回答我的问题。
我在 @Service
class:
中有这个方法
@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 个线程。
一种解决方案是获取父线程抛出的异常(但是太麻烦了,我不知道如何实现)。
感谢您的帮助。
如果您真的想异步工作,那么您很可能使用了错误的工具 - 最好切换到 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;
}
}
有一个非常相似的问题
我在 @Service
class:
@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 个线程。
一种解决方案是获取父线程抛出的异常(但是太麻烦了,我不知道如何实现)。
感谢您的帮助。
如果您真的想异步工作,那么您很可能使用了错误的工具 - 最好切换到 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;
}
}