@Async 生效需满足三条件:方法 public、非本类自调用、类被 Spring 容器管理;常见失效原因为本类内调用、非 public 方法、未启用 @EnableAsync、返回类型不符、new 创建对象及默认线程池不安全。

@Async 要生效,必须满足三个硬性条件:方法必须是 public、不能在同一个类中自调用、所在类需被 Spring 容器管理(即加了 @Service 或 @Component 等)。缺一不可,否则静默失效——连日志都不会打。
为什么 @Async 方法没走异步?常见失效场景
最常踩的坑是「本类内调用」:doTaskA() 标了 @Async,但被同一个类里的 doMain() 直接调用,此时 Spring 的代理机制根本不会介入,代码照常同步执行。
其他典型问题包括:
- 方法是
private或protected—— Spring 无法生成代理拦截 - 没开启异步支持:忘记在配置类或启动类上加
@EnableAsync - 返回类型不是
void或Future子类(如CompletableFuture),Spring 会拒绝异步化 - 使用了
new创建的对象,而非从 Spring 容器取 bean
@Async 默认线程池太危险,必须重写
Spring 默认用 SimpleAsyncTaskExecutor,它不复用线程,每次新建线程,高并发下极易 OOM。生产环境必须显式配置线程池。
立即学习“Java免费学习笔记(深入)”;
推荐方式是定义一个 ThreadPoolTaskExecutor Bean,并指定核心参数:
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
然后在方法上指定:@Async("taskExecutor")。不指定名称时,Spring 会找名为 taskExecutor 或 asyncExecutor 的 Bean;找不到才退化为默认实现。
如何捕获 @Async 方法中的异常?
@Async 方法抛出的异常不会传播到调用方,而是被吞掉(除非你用 Future.get() 主动获取)。
两种可靠做法:
- 返回
CompletableFuture,并在调用方用.whenComplete((r, e) -> { if (e != null) log.error("", e); })处理 - 在异步方法内部 try-catch,手动记录日志或发告警,例如:
try { ... } catch (Exception e) { log.error("async task failed", e); throw e; }
注意:Future.get() 是阻塞的,别在 Web 请求主线程里直接调,否则异步就白配了。
真正麻烦的从来不是怎么加 @Async,而是线程上下文丢失(比如 SecurityContext、RequestContextHolder)、事务不生效、以及线程池参数和业务流量不匹配。这些没法靠注解自动解决,得结合 TaskDecorator 或手动透传上下文来处理。










