当前位置: 首页 > news >正文

Spring Web MVC的异步请求解读

文章目录

    • 一、 背景
    • 二、 核心概念:为什么要异步?
    • 三、 三种核心的异步处理方式
      • 1. `Callable` (最简单的异步)
      • 2. `DeferredResult` (解耦生产者与消费者)
      • 3. `ResponseBodyEmitter` / `SseEmitter` (流式响应)
      • 4. 响应式类型 (`Flux` / `Mono`)
    • 四、 深入原理:请求生命周期
    • 五、 Spring Boot 3 的新特性:虚拟线程
    • 六、 配置与异常处理
      • 1. 异步请求超时配置
      • 2. 异步拦截器
      • 3. 异步请求中的异常处理
    • 七、 总结与避坑指南
    • 八、 参考文档

一、 背景

在 Spring Boot 3 (基于 Spring Framework 6 和 Jakarta EE 10) 中,Spring MVC 对异步请求的支持已经达到了非常成熟且深度的整合水平。这不仅仅是简单的“多线程”,而是建立在 Servlet 3.1+ 规范之上的完整异步处理模型。

以下是对 Spring MVC 异步请求处理的全面讲解:


二、 核心概念:为什么要异步?

在传统的同步 Servlet 模型中:

  1. 请求到达,Tomcat 线程池分配一个线程。
  2. 线程执行业务逻辑(如查数据库、调用外部 API)。
  3. 线程在等待 IO 响应期间被阻塞
  4. 响应返回,线程释放。

痛点:高并发下,线程池会被大量等待中的请求耗尽,导致服务无法响应新请求。

Spring MVC 异步处理

  1. 请求到达,Tomcat 线程分配。
  2. 线程触发耗时操作(如返回CallableDeferredResult),立即退出方法,释放回线程池。
  3. 耗时操作在独立线程或 IO 多路复用机制中执行。
  4. 执行完毕后,触发回调,重新请求一个 Tomcat 线程来处理响应。

优势:Tomcat 线程不再被 IO 阻塞,极大地提高了服务器的吞吐能力。


三、 三种核心的异步处理方式

Spring MVC 提供了三种主要的异步返回类型,分别对应不同的业务场景:

1.Callable(最简单的异步)

适用场景:需要在独立线程中执行的耗时任务,Spring 内部会使用配置好的TaskExecutor来执行它。

@GetMapping("/callable")publicCallable<String>processData(){// 这个 lambda 会由 Spring 提交到线程池执行return()->{Thread.sleep(2000);// 模拟耗时return"Callable Result";};}
  • 流程:主线程返回Callable-> Spring 调用线程池执行 -> 等待结果 -> 分发结果给容器。
  • 注意:在 Spring Boot 3.2+ 支持虚拟线程的环境下,Callable可以配置为在虚拟线程中执行,从而避免阻塞平台线程。

2.DeferredResult(解耦生产者与消费者)

适用场景:异步编程模型的核心。适用于需要在不同线程(如消息队列监听器、外部事件)中设置结果的场景。

// 1. 创建一个 DeferredResult 对象,设置超时时间DeferredResult<String>result=newDeferredResult<>(5000L);// 2. 模拟在另一个线程(如 MQ 消费者、定时任务)中设置结果newThread(()->{try{Thread.sleep(2000);}catch(Exceptione){}// 当这里调用 setResult 时,Servlet 容器会重新唤醒请求处理result.setResult("Hello World");}).start();// 3. Controller 方法立即返回,此时还没有结果returnresult;
  • 优势:完全解耦。请求线程和处理线程没有任何直接关联,非常适合网关转发、长轮询等场景。

3.ResponseBodyEmitter/SseEmitter(流式响应)

适用场景:需要分批次、多次发送数据给客户端(如 AI 对话、实时日志推送)。SseEmitterResponseBodyEmitter的子类,专门用于 Server-Sent Events。

@GetMapping("/stream")publicSseEmitterstreamData(){SseEmitteremitter=newSseEmitter();// 在其他线程中向 emitter 发送数据executorService.execute(()->{try{emitter.send("First chunk");Thread.sleep(1000);emitter.send("Second chunk");emitter.complete();// 结束流}catch(Exceptione){emitter.completeWithError(e);}});returnemitter;}

4. 响应式类型 (Flux/Mono)

这是 Spring Boot 3 混合架构的亮点。如果你的 Controller 返回FluxMono,Spring MVC 会自动将其适配为异步处理。

  • 返回Mono:类似于DeferredResultCallable
  • 返回Flux:自动转换为ResponseBodyEmitter流式处理。

四、 深入原理:请求生命周期

当异步请求发生时,Servlet 容器会经历以下阶段:

  1. 初始分发 (REQUEST)

    • Tomcat 接收请求。
    • Spring MVCDispatcherServlet处理请求。
    • Controller 返回异步类型(如Flux)。
    • 关键动作:调用request.startAsync(),开启异步模式。
    • 主线程结束,返回 Tomcat 线程池。
  2. 异步阶段

    • 业务逻辑在后台执行(Reactive 线程池或自定义线程池)。
    • 此阶段不占用 Tomcat 线程
  3. 异步分发 (ASYNC)

    • 当后台任务完成(或Flux发射数据时),Servlet 容器会发起一个新的分发
    • 这个分发的类型是DispatcherType.ASYNC
    • DispatcherServlet再次介入,负责将结果写回响应流。

重点:过滤器 和拦截器 的行为差异。

  • 过滤器:默认只在 REQUEST 阶段执行。如果想拦截 ASYNC 阶段,需在web.xmlFilterRegistrationBean中显式配置DispatcherType.ASYNC
  • 拦截器默认会拦截所有阶段(包括 ASYNC)。这就是为什么你的SaInterceptor会在流式数据写回时被再次触发的原因。

五、 Spring Boot 3 的新特性:虚拟线程

在 Spring Boot 3.2+ 中,如果运行在 JDK 21 上,异步处理有了新玩法。

传统上,异步编程是为了解决线程阻塞问题,但代码写起来复杂(回调地狱)。

虚拟线程让你可以重新写同步阻塞的代码,但获得异步非阻塞的性能:

// Spring Boot 3.2 + JDK 21// 开启虚拟线程后,这个简单的阻塞代码不会阻塞 Tomcat 的平台线程@GetMapping("/blocking-with-virtual")publicStringblocking()throwsInterruptedException{Thread.sleep(1000);// 阻塞的是虚拟线程,底层 OS 线程不阻塞return"Done";}

这改变了异步请求的格局:

  • 简单业务:使用虚拟线程 + 同步代码,不再需要FluxCallable
  • 流式业务:依然需要FluxSseEmitter,因为这是流式数据模型,不仅仅是线程模型。

六、 配置与异常处理

1. 异步请求超时配置

异步请求不能无限等待,默认超时通常是 30 秒。

spring:mvc:async:request-timeout:30000# 毫秒

2. 异步拦截器

为了处理异步生命周期中的事件(如超时、完成),Spring 提供了AsyncHandlerInterceptor

publicclassMyAsyncInterceptorimplementsAsyncHandlerInterceptor{@OverridepublicvoidafterConcurrentHandlingStarted(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){// 在异步处理开始之后调用(主线程退出前)// 可以在这里清理 ThreadLocal,或者记录日志System.out.println("Async handling started, main thread released.");}}

3. 异步请求中的异常处理

异步阶段抛出的异常,会被 Spring MVC 捕获,并通过ASYNC分发转发给异常处理器。

  • 如果返回DeferredResult,可以通过result.setErrorResult(e)处理。
  • 如果返回Flux,异常会导致流终止,并被全局异常处理器捕获(前提是异常处理器能处理 SSE 类型的响应,否则会报 Converter 错误)。

七、 总结与避坑指南

在 Spring Boot 3 中使用 Spring MVC 异步请求:

  1. 首选响应式类型:如果涉及到流式输出(SSE),Flux是最优雅的方案。
  2. 警惕 ThreadLocal:任何基于ThreadLocal的组件(Sa-Token, Spring Security, MDC 日志追踪)在异步分发阶段都会失效。
    • 解决方案:要么使用AsyncHandlerInterceptor手动传播上下文;要么在主线程入口处提取数据,显式传参(推荐)。
  3. 拦截器配置:必须显式排除DispatcherType.ASYNC,除非你明确需要在数据写回时做拦截。
  4. 过滤器配置:如果你需要在 ASYNC 阶段也执行某些过滤器(如重新绑定上下文),记得配置dispatcherTypes包含ASYNC

理解了“REQUEST -> startAsync -> ASYNC Dispatch”这一流程,你就掌握了 Spring MVC 异步处理的核心密码。


八、 参考文档

  • 异步请求 (Asynchronous Requests) | Spring Framework6.1.18中文文档|Spring官方文档|SpringBoot 教程|Spring中文网
http://www.jsqmd.com/news/468574/

相关文章:

  • 2026年常见网页爬取住宅代理服务商整理与选择参考
  • 通信中继无人机市场前景明朗:未来六年复合年增长率锁定7.9%
  • 深入理解 RLHF/PPO/DPO/GRPO
  • Day 2:信号槽连接方式对比 - 实战练习题
  • OpenClaw:完全零成本在Windows本机部署OpenClaw免费大模型指南
  • 装好就能住的装修哪家精选
  • LangGraph vs Semantic Kernel:状态图与内核插件的两条技术路线对比
  • CSMS VS ISMS管理体系
  • iOS 审核 4.3a 被拒 【三大禁忌】
  • spring boot 打包教程
  • Spring Boot博客系统集成AI智能摘要功能实战
  • 基于SpringBoot+Vue的智慧校园升学就业系统毕设项目(完整源码+论文+部署)
  • OpenClaw(龙虾)本地部署
  • Windows经典漏洞-MS17-010(学习分享)
  • 苹果Watch心率监测技术详解及优化建议
  • 【超全】基于微信小程序的生鲜销售系统【包括源码+文档+调试】
  • 基于YOLO26的智慧教育场景的学生课堂行为实时分析系统|完整源码+PyQt5界面+训练与部署全流程
  • 手机号中间四位隐藏,SQL函数来实现。
  • 把做网站这事交给纯AI建站+小龙虾,会是怎样的一个惊喜呢?
  • 2026年上海注册公司代理记账口碑TOP10,哪家服务更胜一筹?? - 企业推荐官【官方】
  • 【领】系统集成小计算题练习册47页PDF(有/无答案版)
  • 基于SpringBoot+Vue的美剧观影网站毕设项目(完整源码+论文+部署)
  • 小米电视玩家常用的完整优化方案
  • Kinaxis宣布修订常规发行人回购计划,将其规模扩大至上限
  • 中科院联合北大首创分层机器人
  • Linux39:OPENCV图像叠加
  • Docker Compose down 后是否需要手动删除容器?一次完整实践说明
  • 链表----回文链表
  • Flutter 三方库 asset_opt 的鸿蒙化适配指南 - 让应用资源“瘦身有术”,打造鸿蒙应用专家级的资产优化自动化工作流
  • 一波带走,SpringBoot 中的各种参数校验方案汇总