@async异常默认无法被@controlleradvice捕获,需通过asyncuncaughtexceptionhandler全局处理;推荐返回future/completablefuture主动感知异常;自定义线程池应配置rejectedexecutionhandler兜底。

Spring Boot 中异步方法(@Async)抛出的异常默认不会被 @ControllerAdvice 捕获,因为异常发生在独立线程中,与 Web 请求线程完全隔离。要实现全局捕获,必须走 Spring 异步框架自身的异常处理机制,核心是 AsyncUncaughtExceptionHandler。
用 AsyncUncaughtExceptionHandler 捕获未处理异常
这是最直接、最标准的方式,专门用于捕获那些没有被方法内部 try-catch、也没有返回 Future 做显式 get() 的异步任务异常。
- 创建一个配置类,实现
AsyncConfigurer接口 - 重写
getAsyncUncaughtExceptionHandler()方法,返回自定义处理器 - 在处理器中记录日志、上报监控、触发告警等,但不能影响原线程执行流(它只是“旁观者”)
示例:
public class AsyncConfig implements AsyncConfigurer {@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
log.error("Async task failed in method: {} with params: {}", method, params, ex);
// 可扩展:发钉钉/企业微信告警、写入ELK、推入死信队列等
};
}
}
配合 Future 或 CompletableFuture 主动感知异常
如果异步方法有返回值,推荐用 Future<t></t> 或 CompletableFuture<t></t>,这样调用方可以主动检查结果或异常,把异常“拉回”主流程统一处理。
- 方法签名改为
public Future<string> doAsyncTask()</string> - 调用后调用
future.get()(会阻塞并抛出 ExecutionException 包装的原始异常) - 或用
CompletableFuture.supplyAsync(...).exceptionally(...)做链式异常处理
这种方式更适合需要结果反馈或强一致性的场景,比如异步校验后决定是否提交事务。
避免异常丢失的线程池级兜底
即使配置了 AsyncUncaughtExceptionHandler,若线程池本身拒绝任务(如 TaskRejectedException),该异常仍可能逃逸。因此建议:
- 自定义线程池时,设置合理的
RejectedExecutionHandler,例如记录日志+降级策略(如同步执行或丢弃并告警) - 不要依赖默认的
AbortPolicy(直接抛异常),改用CallerRunsPolicy或自定义策略 - 在
AsyncConfigurer的getAsyncExecutor()中一并配置
不推荐:靠 @ControllerAdvice 拦截 @Async 异常
@ControllerAdvice 只对 Controller 层抛出的异常生效,而 @Async 方法运行在线程池线程中,与 HTTP 请求生命周期无关。试图用 AOP 或环绕通知去“拦截”异步方法异常,不仅复杂、易出错,还会破坏 Spring 的代理机制,得不偿失。











