《Java 100 天进阶之路》第68篇:JavaWeb核心技术之Servlet(2026版)
第68篇:JavaWeb核心技术之Servlet(2026版)
📌 系列导航:《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第67篇:JVM面试压轴题 |
➡️ 下一篇:第69篇:JSP与EL表达式
🗺️ 本文阅读地图(3 分钟速览)
JVM调优专题已收官,本篇开启JavaWeb专题。Servlet是Java Web开发的“地基”——Spring MVC、Spring Boot底层都是Servlet。理解Servlet,才能真正理解Web框架:
| 模块 | 核心问题 | 一句话回答 |
|---|---|---|
| Servlet是什么 | Java Web的“地基”是什么? | 运行在Web容器中的Java类,处理HTTP请求并返回响应 |
| 生命周期 | Servlet从生到死经历什么? | 加载→实例化→init()→service()→destroy() |
| 核心对象 | 请求和响应用什么对象? | HttpServletRequest(请求)、HttpServletResponse(响应) |
| 三大组件 | Servlet规范还有哪些组件? | Servlet + Filter + Listener,三者分工协作 |
| 异步处理 | Servlet 3.0带来了什么? | AsyncContext实现非阻塞处理,释放容器线程 |
| Servlet vs Spring MVC | 现在还用Servlet吗? | Spring MVC底层仍是Servlet,但封装了细节 |
| Jakarta EE迁移 | javax包去哪了? | 从javax.servlet迁移到jakarta.servlet(Jakarta EE 9+) |
| 面试最爱问 | 高频考点有哪些? | 见文末 🎤 小节 |
一、核心知识点
1. Servlet是什么?
Servlet(Server Applet)是Java EE(现Jakarta EE)规范中定义的服务器端组件,用于接收客户端请求并返回响应。它不依赖特定的平台或协议,最常用的是HTTP Servlet。
💡 本质:Servlet就是一个运行在Web容器(Tomcat、Jetty等)中的Java类,遵循特定的生命周期规范。
2. Servlet的核心地位
Servlet是Java Web一切框架的底层基石:

无论是Spring MVC的DispatcherServlet,还是Spring Boot的内嵌Tomcat,底层都是Servlet。理解Servlet,是理解一切Java Web框架的前提。
3. 2026年重要背景:Jakarta EE迁移
从Jakarta EE 9开始,所有javax.servlet包名改为jakarta.servlet。Servlet API版本演进:
- Servlet 4.0(Java EE 8):
javax.servlet - Servlet 5.0+(Jakarta EE 9+):
jakarta.servlet - Servlet 6.0(Jakarta EE 10+):去除
SecurityManager相关API
💡 面试注意:2026年,新建项目应使用
jakarta.servlet,老项目迁移需注意包名变更。
4. Servlet三大核心组件
| 组件 | 作用 | 典型场景 |
|---|---|---|
| Servlet | 处理具体业务请求 | 用户登录、订单查询 |
| Filter | 请求/响应预处理和后处理 | 日志记录、编码过滤、权限校验 |
| Listener | 监听Web应用生命周期事件 | 应用启动初始化、Session管理 |
二、通俗讲解(1分钟开心学)
把Servlet想象成餐厅的“服务员”
-
客户端(浏览器) :顾客,坐在桌前要点菜(发请求)。
-
Servlet:服务员,负责接收顾客的点菜单(请求),去厨房(业务逻辑)拿菜,再把菜端给顾客(响应)。每个服务员只服务一位顾客(一个请求对应一个Servlet实例的service方法调用)。
-
Filter:餐厅门口的迎宾员。每个顾客进来,先经过迎宾员检查——带口罩了吗?有预约吗?(请求过滤),走的时候迎宾员再说声“欢迎下次光临”(响应后处理)。
-
Listener:餐厅的传感器。餐厅开门了(应用启动),传感器记录;有顾客进来了(Session创建),传感器记录;餐厅关门了(应用关闭),传感器记录。
-
Web容器(Tomcat) :餐厅的管理系统,负责招聘服务员(创建Servlet实例)、安排工作(调用生命周期方法)、辞退服务员(销毁Servlet)。
一句话总结:Filter管“进出门”,Servlet管“核心服务”,Listener管“全程监控”。
三、Servlet生命周期
Servlet生命周期由Web容器(Tomcat等)全权管理,开发者不需要手动new Servlet对象。
3.1 生命周期四阶段

各阶段详解:
| 阶段 | 方法 | 调用次数 | 触发时机 | 说明 |
|---|---|---|---|---|
| 加载与实例化 | 构造方法 | 1次 | 第一次请求到达(默认)或容器启动时 | 默认懒加载,可通过<load-on-startup>改为容器启动时加载 |
| 初始化 | init() |
1次 | 实例化后立即调用 | 加载数据库连接池、配置文件等资源 |
| 服务 | service() |
每次请求 | 每次HTTP请求到达 | 根据请求方法分发到doGet()/doPost()等 |
| 销毁 | destroy() |
1次 | 容器关闭或应用卸载时 | 释放资源,如关闭数据库连接 |
💡 重要理解:Servlet是单例多线程——整个应用只有一个Servlet实例,但每个请求会创建一个新线程调用
service()方法。因此Servlet实例变量不是线程安全的!
3.2 代码示例
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.*;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
// ① 初始化(执行一次)
@Override
public void init() throws ServletException {
System.out.println("Servlet初始化,加载资源");
}
// ② 服务(每次请求执行)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<h1>Hello, Servlet!</h1>");
out.println("<p>当前时间:" + new java.util.Date() + "</p>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// POST请求处理
doGet(req, resp);
}
// ③ 销毁(执行一次)
@Override
public void destroy() {
System.out.println("Servlet销毁,释放资源");
}
}
web.xml配置方式(传统,仍用于Servlet 6.0):
<web-app>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup> <!-- 容器启动时加载 -->
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
四、核心对象:Request & Response
4.1 HttpServletRequest——请求对象
| 方法 | 作用 |
|---|---|
getParameter(String name) |
获取单个请求参数 |
getParameterMap() |
获取所有请求参数Map |
getHeader(String name) |
获取请求头 |
getSession() |
获取/创建Session |
getRequestDispatcher() |
请求转发 |
setAttribute(String, Object) |
设置请求属性(转发传递数据) |
startAsync() |
Servlet 3.0启动异步处理 |
4.2 HttpServletResponse——响应对象
| 方法 | 作用 |
|---|---|
setContentType(String) |
设置响应内容类型(如text/html;charset=UTF-8) |
setStatus(int) |
设置HTTP状态码 |
getWriter() |
获取字符输出流(输出HTML) |
getOutputStream() |
获取字节输出流(输出文件/图片) |
sendRedirect(String) |
重定向(302) |
addCookie(Cookie) |
添加Cookie |
4.3 请求转发 vs 重定向
| 对比 | 请求转发(forward) | 重定向(sendRedirect) |
|---|---|---|
| 浏览器地址栏 | 不变 | 变化 |
| 请求次数 | 1次(服务器内部) | 2次(客户端重新发起) |
| 数据共享 | ✅ 可共享request属性 | ❌ 不可共享 |
| 性能 | 更高(少一次请求) | 较低(多一次请求) |
| 适用场景 | 服务器内部跳转 | 页面跳转、防止表单重复提交 |
五、Servlet 3.0+ 异步处理
5.1 为什么需要异步?
传统Servlet模型中,一个请求占用一个容器线程直到处理完成。如果业务有耗时操作(如调用外部API、大文件处理),容器线程会被长时间占用,导致并发能力下降。
Servlet 3.0引入异步处理:将耗时任务交给新线程,释放容器线程处理其他请求,提升吞吐量。
5.2 异步Servlet实战
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.util.concurrent.*;
@WebServlet(value = "/async", asyncSupported = true) // 关键:启用异步
public class AsyncServlet extends HttpServlet {
private ExecutorService executor = Executors.newFixedThreadPool(10);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1. 启动异步上下文
AsyncContext ctx = req.startAsync(); //
// 2. 提交耗时任务到线程池
executor.submit(() -> {
try {
// 模拟耗时操作(如调用外部API、查询数据库)
Thread.sleep(3000);
// 3. 获取响应并输出
HttpServletResponse response =
(HttpServletResponse) ctx.getResponse();
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().println("异步处理完成!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4. 完成异步处理
ctx.complete();
}
});
// 容器线程立即释放,不阻塞!
}
}
💡 关键点:
asyncSupported = true启用异步,startAsync()获取AsyncContext,耗时任务在新线程执行,完成后调用complete()。
六、三大组件:Filter & Listener
6.1 Filter——请求拦截器
Filter是Servlet规范的一部分,在请求到达Servlet之前和响应返回客户端之后执行。
典型场景:日志记录、统一编码处理、登录验证、跨域处理、敏感词过滤。
import jakarta.servlet.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;
@WebFilter("/*") // 拦截所有请求
public class LogFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("Filter初始化");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
// 前置处理
System.out.println("请求到达:" + System.currentTimeMillis());
// 放行到下一个Filter或Servlet
chain.doFilter(req, resp);
// 后置处理
System.out.println("响应返回:" + System.currentTimeMillis());
}
@Override
public void destroy() {
System.out.println("Filter销毁");
}
}
Filter链执行顺序:多个Filter按@WebFilter的filterName字母顺序或web.xml配置顺序执行,形成责任链。
6.2 Listener——事件监听器
Listener监听Web应用中关键对象的生命周期事件。
三种核心Listener:
| Listener | 监听对象 | 典型场景 |
|---|---|---|
ServletContextListener |
应用上下文(启动/关闭) | 加载配置文件、初始化数据库连接池 |
HttpSessionListener |
Session(创建/销毁) | 在线用户统计 |
ServletRequestListener |
请求(创建/销毁) | 请求日志记录 |
import jakarta.servlet.*;
public class AppListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("应用启动,加载配置...");
// 加载数据库连接池、Spring容器等
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("应用关闭,释放资源...");
}
}
七、Servlet vs Spring MVC
| 对比维度 | Servlet | Spring MVC |
|---|---|---|
| 定位 | Java Web底层规范 | 基于Servlet的高级Web框架 |
| 开发方式 | 继承HttpServlet,手动配置映射 |
注解驱动(@Controller、@RequestMapping) |
| 参数绑定 | 手动req.getParameter() |
自动绑定到方法参数 |
| 视图渲染 | 手动拼接HTML或用JSP | 自动渲染,支持多种视图技术 |
| 启动速度 | 快 | 较慢(需加载Spring上下文) |
| 内存占用 | 低 | 高 |
| 适用场景 | 理解底层、轻量级应用 | 绝大部分企业级应用 |
💡 核心结论:Spring MVC底层是
DispatcherServlet,它本身就是一个Servlet。掌握了Servlet,就掌握了Spring MVC的“发动机”。
八、避坑要点
| 错误/误区 | 后果 | 正确做法 |
|---|---|---|
| Servlet实例变量非线程安全 | 多线程数据混乱 | 尽量用局部变量,避免在Servlet中定义可变的实例变量 |
忘记asyncSupported = true |
异步功能不生效 | 注解中加上asyncSupported = true |
Filter中不调用chain.doFilter() |
请求被拦截,无法到达目标 | 必须调用chain.doFilter()放行 |
| 请求转发和重定向混淆 | 数据丢失或重复提交 | 转发用forward,重定向用sendRedirect |
忘记设置setContentType |
中文乱码 | 统一设置text/html;charset=UTF-8 |
老项目使用javax.servlet |
不兼容Jakarta EE 9+ | 新项目使用jakarta.servlet |
九、面试高频考点
Q1:Servlet的生命周期有哪些阶段?
四个阶段:①加载与实例化(容器创建Servlet实例);②初始化(调用
init(),执行一次);③服务(调用service(),每次请求执行);④销毁(调用destroy(),执行一次)。
Q2:Servlet是单例还是多例?线程安全吗?
Servlet是单例,整个应用只有一个实例。线程不安全——多个请求共享同一个Servlet实例,实例变量会被多线程共享。因此应避免在Servlet中使用可变的实例变量,尽量使用局部变量。
Q3:Filter和Interceptor的区别?
Filter是Servlet规范的一部分,运行在Web容器中,在请求到达Servlet之前执行。Interceptor是Spring MVC的组件,运行在Spring容器中,在Handler执行前后执行。Filter更底层,Interceptor功能更丰富(可注入Spring Bean)。
Q4:请求转发(forward)和重定向(redirect)的区别?
转发(forward)是服务器内部跳转,地址栏不变,1次请求,可共享request属性。重定向(redirect)是客户端重新发起请求,地址栏变化,2次请求,不可共享request属性。
Q5:javax.servlet和jakarta.servlet有什么区别?
Jakarta EE 9开始,所有
javax包名改为jakarta。新项目使用jakarta.servlet。老项目迁移需修改import和依赖版本。
🎤 面试官追问陷阱(加分题)
追问1:“Servlet 3.0的异步处理中,startAsync()之后容器线程会怎样?”
👉 容器线程立即释放,回到线程池处理其他请求。耗时任务在
AsyncContext关联的新线程中执行。完成后调用complete()通知容器。
追问2:“<load-on-startup>设置为正数和负数有什么区别?”
👉 正数:容器启动时加载Servlet,数值越小优先级越高。负数或未设置:懒加载——第一次请求到达时才创建Servlet实例。
十、练习题
-
代码题:编写一个Servlet,接收
name参数,返回“Hello, [name]!”的HTML页面。 -
分析题:Filter和Servlet的执行顺序是什么?一个请求经过多个Filter时,顺序如何确定?
-
场景题:某Web应用需要统计在线用户数,应该使用什么组件?如何实现?
📊 你的学习进度
- 当前:第68篇 / 共108篇 · 进阶篇:JavaWeb核心技术(第68~72篇)
- ✅ 已完成:基础篇44篇 + 第45~68篇
- 📖 正在学:第68篇
- ⏳ 待学习:第69~108篇
👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇
👉 下一篇文章预告
🚀 下一篇:《第69篇:JSP与EL表达式》
内容简介:JSP原理、九大内置对象、EL表达式语法、JSTL标签库、JSP与Servlet的关系。
👉 JavaWeb专题持续深入,拿下JSP与EL!
📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!
更多推荐



所有评论(0)