1. 引言

本文将深入探讨 Spring MVC 中的 @Async 注解机制,并对比介绍 Spring WebFlux 的响应式编程模型。目标是帮助你清晰理解两者在异步处理、线程模型和 I/O 模式上的本质区别,避免在项目选型时“踩坑”。

2. 实现场景设计

为了直观对比,我们设计一个简单的 Web 接口场景:

  • 单个接口返回字符串结果
  • 请求经过一个 Filter,模拟 200ms 延迟
  • Controller 处理耗时 500ms(模拟业务计算)
  • 使用 Apache ab 发起并发请求(-c 40,共 1600 次)
  • 通过 JConsole 实时监控线程状态

⚠️ 注意:本文不是性能压测报告,重点在于观察线程调度行为和 I/O 模型差异。

3. Spring MVC Async 原理

Spring 3.0 引入 @Async,用于将耗时任务提交到独立线程执行。从 Spring 3.2 起,结合 Servlet 3.0 的异步支持,@Async 可用于 @Controller@RestController,实现 Web 层异步化。

其核心流程如下:

✅ 请求进入 Filter 链
✅ 到达 DispatcherServlet 后,开启异步模式(调用 AsyncWebRequest#startAsync
✅ 将请求交给 WebAsyncManager 管理,Servlet 线程释放
✅ Filter 链反向执行(清理资源)
WebAsyncManager 提交任务到 ExecutorService
✅ 任务完成,通知 DispatcherServlet 返回响应

返回类型必须是 FutureCompletableFutureListenableFuture,不能是 void

4. Spring Async 实现

主应用类

启用异步支持只需添加 @EnableAsync

@SpringBootApplication
@EnableAsync
public class AsyncVsWebFluxApp {
    public static void main(String[] args) {
        SpringApplication.run(AsyncVsWebFluxApp.class, args);
    }
}

AsyncFilter

模拟 200ms 延迟:

@Component
public class AsyncFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
        try {
            Thread.sleep(200); // 模拟处理耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

AsyncController

使用 @Async 返回 CompletableFuture

@RestController
public class AsyncController {
    @GetMapping("/async_result")
    @Async
    public CompletableFuture<String> getResultAsync(HttpServletRequest request) {
        try {
            Thread.sleep(500); // 模拟业务耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return CompletableFuture.completedFuture("Result is ready!");
    }
}

测试结果

启动应用并执行压测:

ab -n 1600 -c 40 http://localhost:8080/async_result

JConsole 显示线程数明显上升,说明 @Async 使用了线程池来处理请求,属于 异步阻塞 I/O 模型。

Async jconsol
Async ab

5. Spring WebFlux 简介

Spring 5.0 推出 WebFlux,基于 Reactor 实现响应式编程,支持非阻塞 I/O 和响应式背压(reactive backpressure)。

关键特性:

✅ 支持 Servlet 3.1+ 的非阻塞 I/O
✅ 可运行在 Netty、Undertow、Tomcat、Jetty 等容器
✅ 基于 Reactor 的 MonoFlux 提供声明式 API
✅ 支持函数式编程风格的接口定义

⚠️ 注意:WebFlux 的优势在于高并发、低延迟场景,对慢客户端更友好。

6. Spring WebFlux 实现

主应用类

无需额外注解,Spring Boot 自动识别 WebFlux 依赖:

@SpringBootApplication
public class AsyncVsWebFluxApp {
    public static void main(String[] args) {
        SpringApplication.run(AsyncVsWebFluxApp.class, args);
    }
}

WebFluxFilter

实现 WebFilter,使用 Mono.delay 模拟延迟:

@Component
public class WebFluxFilter implements org.springframework.web.server.WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        return Mono
          .delay(Duration.ofMillis(200))
          .then(webFilterChain.filter(serverWebExchange));
    }
}

WebFluxController

返回 Mono<String>,延迟 500ms 模拟处理:

@RestController
public class WebFluxController {

    @GetMapping("/flux_result")
    public Mono<String> getResult(ServerHttpRequest request) {
       return Mono.defer(() -> Mono.just("Result is ready!"))
         .delaySubscription(Duration.ofMillis(500));
    }
}

测试结果

执行相同压测命令:

ab -n 1600 -c 40 http://localhost:8080/flux_result

JConsole 显示线程数稳定,仅使用少量事件循环线程,体现非阻塞特性。

WebFlux jconsole
WebFlux ab

7. 核心差异对比

特性 Spring MVC Async Spring WebFlux
I/O 模型 阻塞 ✅ 非阻塞
请求体读取 阻塞 ✅ 非阻塞
Filter/Servlet 模式 同步 ✅ 全异步
支持容器 Servlet 3.0+ Servlet 3.1+、Netty、Undertow 等
背压支持
编程模型 命令式 + 异步 ✅ 声明式 + 响应式
线程模型 线程池(每请求一线程) 事件循环(少量线程处理高并发)

关键结论

  • Spring WebFlux 更适合 I/O 密集型、高并发场景,能以更少资源支撑更大吞吐
  • Spring MVC Async 适合已有 MVC 项目做异步化改造,学习成本低,过渡平滑
  • 若系统主要为 CPU 密集型任务,两者差异不大,传统 MVC + 线程池可能更简单粗暴

选型建议:不要为了“响应式”而响应式。现有系统稳定,QPS 不高,别轻易重构到 WebFlux,容易“画虎不成反类犬”。

8. 总结

本文通过实际代码和线程监控,对比了 Spring MVC Async 与 Spring WebFlux 在异步处理上的实现机制和行为差异。

  • @Async异步阻塞方案,依赖线程池,适合轻量级异步任务
  • WebFlux 是响应式非阻塞方案,基于事件驱动,适合高并发、低延迟场景

项目选型应结合团队技术栈、系统负载和运维复杂度综合判断。对于大多数中等规模系统,Spring MVC + @Async 完全够用;而对弹性伸缩、资源敏感型服务,WebFlux 才是更优解。

完整代码示例已托管至 GitHub:


原始标题:Spring MVC Async vs Spring WebFlux | Baeldung