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

一篇文章彻底搞懂servlet容器

1. Servlet 容器

Servlet 容器可以理解为 Java Web 应用的“运行环境”和“管家”。我们写的 Servlet、Filter、Listener、Spring MVC 控制器等,最终都不是自己直接监听端口、解析 HTTP,而是交给 Servlet 容器来调度。

常见 Servlet 容器有Tomcat、Jetty、Undertow,其中 Tomcat 最常见。

Servlet 容器的核心职责包括:

  1. 监听网络端口

    例如 Tomcat 监听8080端口,接收客户端 HTTP 请求。

  2. 解析 HTTP 请求

    把底层网络数据解析成 Java 中的HttpServletRequest对象。

  3. 创建响应对象

    为当前请求创建对应的HttpServletResponse对象。

  4. 根据 URL 找到对应 Servlet

    根据配置或注解,例如@WebServlet("/hello"),找到应该处理该请求的 Servlet。

  5. 管理 Servlet 生命周期

    包括:

    加载 Servlet 类 ↓ 实例化 Servlet ↓ 调用 init() ↓ 请求到来时调用 service() ↓ service() 分发到 doGet() / doPost() ↓ 应用关闭时调用 destroy()
  6. 管理线程池

    每个请求通常会交给线程池中的一个工作线程处理。

  7. 管理 Web 应用上下文

    包括 Session、Cookie、Filter、Listener、静态资源等。

2.servlet的生命周期

一个 Servlet 通常经历三个核心阶段:

publicclassHelloServletextendsHttpServlet{@Overridepublicvoidinit(){// 容器创建 Servlet 后调用一次}@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp){// 每次 GET 请求调用}@Overridepublicvoiddestroy(){// 容器关闭或应用卸载时调用一次}}

重点是:Servlet 实例通常是单例的,但会被多个线程同时访问。

所以不要在 Servlet 成员变量里放请求级状态,例如:

public class BadServlet extends HttpServlet { private String username; // 危险:多个请求线程共享 protected void doGet(HttpServletRequest req, HttpServletResponse resp) { username = req.getParameter("username"); }

}
应该把请求相关数据放在局部变量、request、session 或业务对象里。

3. Servlet 容器和 Web 服务器的区别

很多人会混淆Web 服务器Servlet 容器

简单说:

类型例子主要职责
Web 服务器Nginx、Apache HTTP Server处理 HTTP、静态资源、反向代理
Servlet 容器Tomcat、Jetty、Undertow运行 Java Servlet/JSP/Web 应用
应用服务器WebLogic、WebSphere、JBoss/WildFlyServlet + EJB + JMS + JTA 等完整 Jakarta EE 能力

Tomcat 既能接收 HTTP 请求,也能运行 Servlet,所以经常被直接当作 Web 服务器使用。但严格来说,它主要是一个Servlet 容器

生产环境中常见架构是:

用户 ↓ Nginx ↓ Tomcat / Spring Boot 内嵌容器 ↓ Java 应用

Nginx 负责 HTTPS、负载均衡、静态资源、反向代理;Tomcat 负责运行 Java Web 应用。

4. Servlet 容器和程序中的 Servlet 是什么关系?

二者是:

Servlet 容器 = 管理者 / 调度者 / 运行环境 Servlet = 被管理的请求处理组件

也就是说,Servlet 本身不会直接监听端口,也不会自己接收 HTTP 请求。它需要运行在 Servlet 容器中,由容器负责调用。

例如:

@WebServlet("/hello") public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { // 处理 /hello 请求 } }

当浏览器访问/hello时,流程大致是:

浏览器访问 /hello ↓ Tomcat 接收到 HTTP 请求 ↓ Tomcat 找到映射到 /hello 的 HelloServlet ↓ Tomcat 创建或复用 HelloServlet 实例 ↓ Tomcat 调用 service() ↓ service() 分发到 doGet() ↓ HelloServlet 处理请求并生成响应

所以:

Tomcat / Jetty / Undertow ↓ 管理、调度、调用 你的 Servlet

5. Spring Boot 里的 Servlet容器

在 Spring Boot Web 应用中,默认会内嵌一个 Servlet 容器,例如 Tomcat。

启动方式通常是:

java -jar app.jar

Spring Boot 启动时,会在 JVM 内部启动内嵌 Tomcat,并把 Spring MVC 的核心 Servlet,也就是DispatcherServlet,注册到 Servlet 容器中。

请求流程大致如下:

HTTP 请求 ↓ Tomcat ↓ Filter ↓ DispatcherServlet ↓ Controller ↓ Service ↓ Repository ↓ 响应

所以 Spring MVC 本质上仍然是运行在 Servlet 体系之上的。

6. Servlet 容器中的线程模型

以 Tomcat 为例,它通常维护一个线程池。

大致流程是:

客户端连接 ↓ Connector 接收请求 ↓ 工作线程处理请求 ↓ 调用 Filter / Servlet / Controller ↓ 返回响应

这意味着:如果你的业务代码里有很慢的数据库查询、远程调用、文件操作,就会占用 Tomcat 工作线程。

如果线程都被占满,新请求就只能排队,甚至超时。

所以在高并发场景下,常见调优点包括:

最大线程数 连接数 请求队列长度 超时时间 数据库连接池大小 接口响应时间

不过不要一上来就盲目调大线程数。线程越多,CPU 上下文切换、内存占用也越大。

Spring Boot 中 Servlet 是单例,请求对象是否独立?

是的。

在 Spring Boot / Servlet 容器中:

DispatcherServlet / Controller / Service:通常是单例,共享 HttpServletRequest:每个请求独立 HttpServletResponse:每个请求独立 高并发场景下可以理解为: 始终显示详情 请求 A → 线程 1 → requestA / responseA → 同一个 DispatcherServlet 请求 B → 线程 2 → requestB / responseB → 同一个 DispatcherServlet 请求 C → 线程 3 → requestC / responseC → 同一个 DispatcherServlet 也就是说: Servlet 是共享的 request 是每个请求独立的 response 是每个请求独立的

例如:

@GetMapping("/hello") public String hello(HttpServletRequest request) { String name = request.getParameter("name"); return "hello " + name; }

这里的 request 是当前请求自己的对象。请求 A 的参数不会跑到请求 B 中。

7.Controller 成员变量的并发问题

Spring Boot 中的 Controller 默认也是单例 Bean。

例如:

@RestController public class UserController { private String username; // 危险:所有请求共享 @GetMapping("/user") public String user(HttpServletRequest request) { username = request.getParameter("username"); return username; } }

在高并发情况下,可能出现:

请求 A 设置 username = "Tom" 请求 B 设置 username = "Jerry" 请求 A 返回时读到的可能已经变成 "Jerry" 这是因为 username 是 Controller 对象的成员变量,而 Controller 是单例的,所有请求共享同一个对象。

正确写法:

@RestController public class UserController { @GetMapping("/user") public String user(HttpServletRequest request) { String username = request.getParameter("username"); return username; }

}
局部变量存在于当前线程的栈中,不会被其他请求线程共享。

http://www.jsqmd.com/news/939594/

相关文章:

  • 【2026最新】ZLibrary官网镜像入口,一键直达
  • 2026年YXB51:YX76-305-915、YXB48-200-600、YXB51-283-850、YXB65-165-555选择指南 - 优质品牌商家
  • COM3D2.MaidFiddler终极指南:实时女仆编辑器让你完全掌控游戏体验
  • 告别重复操作:用AI视觉语言模型UI-TARS-desktop实现自然语言控制电脑
  • 计算机毕业设计之基于大数据的电商推荐系统研究
  • IDC + 魔力象限:低代码市场与技术双维度选型指南
  • 别猜了,Shopify 博客每天最佳发布时间就是“让它自动发”
  • ZYNQ开发避坑指南:PS与DDR数据不同步?手把手教你搞定Cache一致性问题
  • 抖音无水印批量下载终极指南:免费获取高清视频与封面素材
  • 开源矢量嵌套终极指南:SVGnest如何革新工业切割效率
  • 如何在macOS上快速创建虚拟PDF打印机:终极完整指南
  • 2026年新消息:盘点五家知名的家禽屠宰脱毛设备销售厂家及其市场定位 - 2026年企业资讯
  • 用Python+OpenCV玩转LFW人脸库:从数据加载到SVM分类的保姆级实战
  • AI一键生成lz4解压工具,快速验证压缩文件处理方案
  • AI 生成关卡,还用游戏自己的物理证明它能通关:funplay-unity-mcp 实战
  • 二叉树专项(三):平衡二叉树、红黑树
  • Zotero-Style:文献管理界面的可视化增强解决方案
  • 假如你从6.2开始备考微软MOS 365认证考试
  • GPT-5.5 核心能力落地与实战应用指南
  • 2507不锈钢铸件技术要点解析及优质供应商实测参考:不锈钢卡箍/不锈钢管件/不锈钢精密铸造/不锈钢船舶配件/不锈钢铸造件/选择指南 - 优质品牌商家
  • 计算机毕业设计之基于Python的火车票管理系统
  • 终极Hackintosh配置指南:如何用OpCore-Simplify在30分钟内完成OpenCore EFI创建
  • 2026 Java 开发环境整合:JDK17+21 + IDEA2026 + Maven+Gradle
  • 别再手动改代码了!用Gem5调试片上网络(NoC)的保姆级实战指南(附脚本)
  • 别再只会用晶振了!手把手教你用LC振荡器给Arduino生成时钟信号(附电路图)
  • 前端学习网站
  • 口碑好的除硬剂优质安全型的生产厂家
  • OptiScaler:你的游戏画面还能更好吗?3个痛点1个解决方案
  • 用Makey Makey与Scratch打造《千与千寻》交互音乐盒:从电路原理到创意实现
  • 没有OPC UA接口的PLC、智能仪表,加智能网关实现OPC UA服务端(含客户端测试)