1. Servlet 容器Servlet 容器可以理解为 Java Web 应用的“运行环境”和“管家”。我们写的 Servlet、Filter、Listener、Spring MVC 控制器等最终都不是自己直接监听端口、解析 HTTP而是交给 Servlet 容器来调度。常见 Servlet 容器有Tomcat、Jetty、Undertow其中 Tomcat 最常见。Servlet 容器的核心职责包括监听网络端口例如 Tomcat 监听8080端口接收客户端 HTTP 请求。解析 HTTP 请求把底层网络数据解析成 Java 中的HttpServletRequest对象。创建响应对象为当前请求创建对应的HttpServletResponse对象。根据 URL 找到对应 Servlet根据配置或注解例如WebServlet(/hello)找到应该处理该请求的 Servlet。管理 Servlet 生命周期包括加载 Servlet 类 ↓ 实例化 Servlet ↓ 调用 init() ↓ 请求到来时调用 service() ↓ service() 分发到 doGet() / doPost() ↓ 应用关闭时调用 destroy()管理线程池每个请求通常会交给线程池中的一个工作线程处理。管理 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 ↓ 管理、调度、调用 你的 Servlet5. Spring Boot 里的 Servlet容器在 Spring Boot Web 应用中默认会内嵌一个 Servlet 容器例如 Tomcat。启动方式通常是java -jar app.jarSpring 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; }}局部变量存在于当前线程的栈中不会被其他请求线程共享。
一篇文章彻底搞懂servlet容器
发布时间:2026/6/3 2:53:00
1. Servlet 容器Servlet 容器可以理解为 Java Web 应用的“运行环境”和“管家”。我们写的 Servlet、Filter、Listener、Spring MVC 控制器等最终都不是自己直接监听端口、解析 HTTP而是交给 Servlet 容器来调度。常见 Servlet 容器有Tomcat、Jetty、Undertow其中 Tomcat 最常见。Servlet 容器的核心职责包括监听网络端口例如 Tomcat 监听8080端口接收客户端 HTTP 请求。解析 HTTP 请求把底层网络数据解析成 Java 中的HttpServletRequest对象。创建响应对象为当前请求创建对应的HttpServletResponse对象。根据 URL 找到对应 Servlet根据配置或注解例如WebServlet(/hello)找到应该处理该请求的 Servlet。管理 Servlet 生命周期包括加载 Servlet 类 ↓ 实例化 Servlet ↓ 调用 init() ↓ 请求到来时调用 service() ↓ service() 分发到 doGet() / doPost() ↓ 应用关闭时调用 destroy()管理线程池每个请求通常会交给线程池中的一个工作线程处理。管理 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 ↓ 管理、调度、调用 你的 Servlet5. Spring Boot 里的 Servlet容器在 Spring Boot Web 应用中默认会内嵌一个 Servlet 容器例如 Tomcat。启动方式通常是java -jar app.jarSpring 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; }}局部变量存在于当前线程的栈中不会被其他请求线程共享。