1. Servlet 概述
Servlet
是 Java EE 规范的一部分,它是一种用于 处理客户端请求并生成动态 Web 内容 的 Java 服务器端组件。Servlet 运行在 Servlet 容器(如 Tomcat、Jetty、JBoss) 中,通常用于开发 Web 应用程序。
2. Servlet 的特点
- 基于 Java,平台无关,可移植性强。
- 生命周期由 Servlet 容器管理,包括加载、初始化、处理请求和销毁。
- 高效,一个 Servlet 线程可处理多个请求,而不是为每个请求创建新实例(采用线程池)。
- 支持会话管理(如
HttpSession
)。 - 可与 JSP 结合使用,实现 MVC 模式中的控制层。
3. Servlet 的生命周期
Servlet 的生命周期由 Servlet 容器(如 Tomcat) 进行管理,主要包括以下阶段:
加载(Load)
- 当 Web 服务器启动或收到第一个请求时,Servlet 容器会加载 Servlet 类。
初始化(init 方法)
- 由容器调用
init(ServletConfig config)
进行初始化,只执行一次。
- 由容器调用
处理请求(service 方法)
- 每次客户端请求时,容器调用
service(HttpServletRequest req, HttpServletResponse resp)
,然后再调用对应的doGet()
或doPost()
处理请求。
- 每次客户端请求时,容器调用
销毁(destroy 方法)
- 当 Servlet 容器关闭或应用程序卸载时,
destroy()
方法被调用,可用于释放资源。
- 当 Servlet 容器关闭或应用程序卸载时,
Servlet 生命周期示意图:
加载 -> init() -> service() -> destroy()
4. 编写一个简单的 Servlet
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.getWriter().println("<h1>Hello, Servlet!</h1>");
}
}
说明:
@WebServlet("/hello")
:定义 Servlet 访问路径http://localhost:8080/hello
doGet()
处理GET
请求,并返回Hello, Servlet!
的 HTML 响应
5. Servlet vs JSP
对比项 | Servlet | JSP |
---|---|---|
本质 | Java 类 | HTML + Java 代码 |
侧重点 | 主要用于业务逻辑 | 主要用于页面展示 |
可读性 | 代码较复杂,不易维护 | 可读性更好,类似 HTML |
执行速度 | 直接编译成 .class ,执行速度快 |
需先转换成 Servlet 再编译,性能稍低 |
适用场景 | 处理请求、调用业务逻辑、返回响应 | 显示动态页面,如 HTML 及用户界面 |
6. Servlet 容器(Web 服务器)
Servlet 需要运行在 支持 Servlet 规范的容器 中,如:
- Apache Tomcat
- Jetty
- JBoss
- GlassFish
这些容器管理 Servlet 的生命周期,并提供 HTTP 请求的支持。
7. Servlet 的应用场景
- 处理用户请求(如登录、注册)
- 生成动态网页内容
- 处理表单提交
- 开发 REST API(使用
HttpServlet
) - 作为控制器(Controller),用于 MVC 设计模式
8.面试高频问答:
1. Servlet 和 JSP 的区别?
对比项 | Servlet | JSP |
---|---|---|
本质 | Java 类 | HTML + Java 代码 |
侧重点 | 主要用于业务逻辑 | 主要用于页面展示 |
可读性 | 代码较复杂,不易维护 | 可读性更好,类似 HTML |
执行速度 | 直接编译成 .class ,执行速度快 |
需先转换成 Servlet 再编译,性能稍低 |
适用场景 | 处理请求、调用业务逻辑、返回响应 | 显示动态页面,如 HTML 及用户界面 |
回答要点:
- Servlet 主要处理请求、编写业务逻辑,JSP 主要用于前端页面展示。
- JSP 最终会被转换成 Servlet,执行效率略低于直接使用 Servlet。
- 现代开发通常采用 Servlet + JSP 或 Spring MVC + Thymeleaf 进行开发。
2. Servlet 的生命周期是怎样的?
Servlet 生命周期包括 4 个阶段:
- 加载(Load): Web 服务器启动时,Servlet 类被加载。
- 初始化(init): 调用
init()
方法,仅执行一次。 - 请求处理(service): 服务器调用
service()
方法,根据请求类型执行doGet()
或doPost()
。 - 销毁(destroy): 服务器关闭时,调用
destroy()
释放资源。
代码示例
@Override
public void init() throws ServletException {
System.out.println("Servlet 初始化...");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().println("处理请求...");
}
@Override
public void destroy() {
System.out.println("Servlet 被销毁...");
}
回答要点:
init()
在 Servlet 加载时执行一次。service()
处理 HTTP 请求,每次请求都会执行。destroy()
在服务器关闭或卸载应用时执行,负责资源清理。
3. Servlet 线程是如何工作的?
Servlet 采用 多线程模型,即 一个 Servlet 实例处理多个请求,避免创建多个实例造成的资源浪费。
- 每个请求都会创建一个新的线程(Worker 线程)。
- 多个线程共享同一个 Servlet 实例,避免高并发下创建大量对象的开销。
- Servlet 不是线程安全的,如果 Servlet 需要共享资源(如全局变量),需使用
synchronized
或ThreadLocal
进行线程同步。
代码示例:
public class ThreadUnsafeServlet extends HttpServlet {
private int count = 0; // 共享变量,可能引发线程安全问题
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
count++; // 非线程安全
resp.getWriter().println("访问次数:" + count);
}
}
解决方案:
- 使用 局部变量(线程安全)。
- 使用
synchronized
关键字 保护共享资源。 - 使用
ThreadLocal
变量,确保每个线程有独立的副本。
4. Servlet 如何获取请求参数?
Servlet 通过 HttpServletRequest
对象获取请求参数:
获取 GET 请求参数:
String name = request.getParameter("name");
System.out.println("获取到的 name 参数:" + name);
示例 URL: http://localhost:8080/hello?name=Tom
获取 POST 请求参数:
BufferedReader reader = request.getReader();
String body = reader.lines().collect(Collectors.joining());
System.out.println("请求体:" + body);
获取所有参数:
Map<String, String[]> params = request.getParameterMap();
params.forEach((key, values) -> System.out.println(key + ": " + Arrays.toString(values)));
回答要点:
request.getParameter("name")
获取单个参数值。request.getParameterMap()
获取所有请求参数。- GET 请求参数来自 URL,POST 请求参数通常在请求体中。
5. 如何在 Servlet 中实现会话管理(Session 机制)?
Servlet 主要通过 Session(HttpSession
) 和 Cookie 实现会话管理。
方法 1:使用 HttpSession
HttpSession session = request.getSession();
session.setAttribute("user", "Tom"); // 存储数据
String user = (String) session.getAttribute("user"); // 获取数据
session.invalidate(); // 销毁 session
特点:
- Session 依赖 JSESSIONID(存储在 Cookie 中)。
- 生命周期默认 30 分钟(可配置)。
- 适用于存储用户信息,如登录状态。
方法 2:使用 Cookie
Cookie cookie = new Cookie("user", "Tom");
cookie.setMaxAge(3600); // 设置 Cookie 过期时间(秒)
response.addCookie(cookie);
获取 Cookie:
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if ("user".equals(cookie.getName())) {
System.out.println("Cookie 值:" + cookie.getValue());
}
}
回答要点:
HttpSession
适用于存储短期用户信息,生命周期受服务器控制。Cookie
适用于存储长期信息(如用户偏好设置),生命周期受浏览器控制。Session
更安全,Cookie
可能被客户端篡改(可使用HttpOnly
限制)。
6. forward
和 redirect
的区别?
对比项 | forward | redirect |
---|---|---|
本质 | 服务器内部跳转 | 客户端跳转 |
地址栏 | 地址栏不变 | 地址栏变化 |
是否需要重新请求 | ❌ 不需要 | ✅ 需要 |
数据共享 | ✅ 可共享 request 作用域数据 | ❌ 不能共享 |
适用场景 | 内部资源跳转 | 跳转到外部网站或新的路径 |
示例:
- 服务器内部跳转(
forward
):
request.getRequestDispatcher("/dashboard.jsp").forward(request, response);
- 客户端跳转(
redirect
):
response.sendRedirect("https://www.google.com");
回答要点:
forward
适用于 同一 Web 应用内部跳转,不刷新地址栏。redirect
适用于 不同 Web 应用之间的跳转,地址栏会更新。
总结
Servlet 是 Java EE 中用于 处理 Web 请求的核心组件,负责接收 HTTP 请求、处理业务逻辑并返回响应。在现代 Java Web 开发中,Servlet 通常与 JSP、Spring MVC、Spring Boot 结合使用,提升开发效率。
如果觉得这篇博客对你有帮助,记得点赞 ⭐、收藏 📌、关注 🚀!