Spring Boot 中 @Async 是如何内部工作的?

发布: (2026年1月10日 GMT+8 14:10)
7 min read
原文: Dev.to

Source: Dev.to

(请提供您希望翻译的正文内容,我将为您翻译成简体中文,并保持原有的格式、Markdown 语法以及技术术语不变。)

Introduction 🚀

你有没有调用过 REST API 时想过:

“为什么这个请求会一直阻塞,直到所有操作都完成?”

现在想象一下发送邮件、生成报告,或调用一个响应缓慢的第三方服务——你真的希望用户一直等下去吗?

这时 @Async 在 Spring Boot 中 就能帮上忙。

简单来说,@Async 让 Spring 能说:

“你先去忙,我在后台处理这个任务。”

在这个简单注解的背后,Spring Boot 使用 代理、线程池和执行器 来异步运行你的代码——了解它的 内部工作原理 能帮助你避免生产环境的 bug 和面试陷阱。

在本篇博客中,你将学到:

  • @Async 在幕后到底做了什么
  • Spring Boot 如何执行异步方法
  • 两个 完整的、端到端的 Java 21 示例
  • 常见错误及最佳实践

核心概念 🧠

@Async 在 Spring Boot 中是什么?

@Async 是 Spring 的注解,能够让方法:

  • 在独立线程 中运行
  • 立即返回 给调用者
  • 使用 TaskExecutor 异步执行逻辑

可以把它想象成线上点餐 🍔:

  1. 你下单(API 调用)
  2. 餐厅在后台准备食物
  3. 你不必在柜台前等候

@Async 的内部工作原理(简要说明)

在内部,Spring Boot 使用 Spring AOP(基于代理的机制)

  1. Spring 为你的 bean 创建一个 代理
  2. 当调用带有 @Async 的方法时,代理拦截该调用。
  3. 代理将方法提交给 TaskExecutor
  4. 执行器在 独立线程 中运行该方法。
  5. 主线程随即继续执行。

关键点@Async 仅在从另一个 Spring 管理的 bean调用该方法时才会生效。

使用场景与收益

✅ 典型使用场景

  • 发送邮件
  • 调用慢速外部 API
  • 后台处理
  • 事件处理
  • 审计日志

🎯 收益

  • 更快的 API 响应
  • 更好的用户体验
  • 更清晰的关注点分离

Source:

端到端设置 (Java 21 + Spring Boot) ⚙️

我们将构建:

  • 一个 REST API
  • 一个异步服务
  • 一个自定义线程池
  • cURL 请求 + 响应

示例 1:基础 @Async 执行

1️⃣ 启用异步支持

@Configuration
@EnableAsync
public class AsyncConfig {
}

这告诉 Spring 去寻找 @Async 方法

2️⃣ 异步服务

@Service
public class NotificationService {

    @Async
    public void sendNotification() throws InterruptedException {
        // Simulate long‑running task
        Thread.sleep(3000);
        System.out.println("Notification sent by thread: " +
                           Thread.currentThread().getName());
    }
}

3️⃣ REST 控制器

@RestController
@RequestMapping("/api")
public class NotificationController {

    private final NotificationService service;

    public NotificationController(NotificationService service) {
        this.service = service;
    }

    @GetMapping("/notify")
    public String triggerNotification() throws InterruptedException {
        service.sendNotification();
        return "Request accepted. Processing asynchronously.";
    }
}

4️⃣ cURL 请求

curl -X GET http://localhost:8080/api/notify

✅ 响应

Request accepted. Processing asynchronously.

API 会立即返回,而任务在后台运行。

示例 2:@AsyncCompletableFuture(推荐)

为什么使用它?

  • 更好的控制
  • 非阻塞的结果处理
  • 更简洁的异步组合

1️⃣ 带返回值的异步服务

@Service
public class ReportService {

    @Async
    public CompletableFuture<String> generateReport() throws InterruptedException {
        Thread.sleep(2000);
        return CompletableFuture.completedFuture("Report generated successfully");
    }
}

2️⃣ REST 控制器

@RestController
@RequestMapping("/api")
public class ReportController {

    private final ReportService service;

    public ReportController(ReportService service) {
        this.service = service;
    }

    @GetMapping("/report")
    public CompletableFuture<String> generate() throws InterruptedException {
        return service.generateReport();
    }
}

3️⃣ cURL 请求

curl -X GET http://localhost:8080/api/report

✅ 响应

Report generated successfully

请求线程会提前释放,而任务异步执行。

自定义线程池(强烈推荐) 🧵

@Configuration
@EnableAsync
public class AsyncExecutorConfig {

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-worker-");
        executor.initialize();
        return executor;
    }
}

若不配置,Spring 会回退到 SimpleAsyncTaskExecutor,这并不适合生产环境。

Best Practices ✅

  • Never call @Async inside the same class – self‑invocation bypasses Spring proxies. → 永远不要在同一个类中调用 @Async – 自调用会绕过 Spring 代理。
  • Always define a custom executor – avoid unbounded thread creation. → 始终定义自定义执行器 – 避免无限制的线程创建。
  • Use CompletableFuture for return values – gives you better async handling and composition. → 使用 CompletableFuture 作为返回值 – 提供更好的异步处理和组合能力。
  • Keep async logic idempotent and exception‑safe; handle failures inside the async method. → 保持异步逻辑 幂等异常安全;在异步方法内部处理失败。
  • Monitor thread‑pool metrics (active threads, queue size) in production. → 在生产环境中监控线程池指标(活动线程数、队列大小)。

Common Mistakes ❌

  • 忘记 @EnableAsync
  • 期望 私有方法具有异步行为
  • 阻塞 异步线程,使用繁重逻辑
  • 使用 @Async 处理 CPU‑密集型任务
  • 假设 事务会自动传播

小贴士

  • 显式处理异常 – 异步异常不会自动传播。
  • 保持异步逻辑轻量@Async 不是消息队列的替代方案。

结论 🧩

@AsyncSpring Boot 中看起来很简单,但内部依赖于:

  • Spring AOP 代理
  • 任务执行器
  • 线程池

了解这些内部实现可以帮助你:

  • 避免细微的 bug
  • 编写可扩展的应用程序
  • 给面试官留下深刻印象 😉

行动号召 📣

💬 对 @Async 或您遇到的异步错误有疑问吗?

🧠 在下方评论——一起讨论!

⭐ 关注获取更多 Java 编程Spring Boot 内部 内容。

Back to Blog

相关文章

阅读更多 »