Servlet 概述
简单来说,Servlet 就是跑在服务器上的 Java 程序,充当客户端(通常是浏览器)和后端逻辑之间的中间层。它的主要职责是扩展服务器功能、处理 HTTP 请求并生成响应。
为什么需要 Servlet?
相比静态 HTML,Servlet 能根据参数或数据库结果动态生成内容。它驻留内存,处理速度快,且具备平台独立性。作为 JSP、Spring MVC 等框架的基石,它的功能非常强大。
Servlet 与 CGI 的比较
| 特性 | CGI | Servlet |
|---|---|---|
| 进程模型 | 每个请求创建新进程 | 多线程处理(轻量级) |
| 性能 | 启动慢,资源消耗大 | 启动快,资源消耗小 |
| 平台 | 依赖语言/平台 | 基于 Java,跨平台 |
| 持久性 | 无 | 驻留内存,可保持状态 |
Servlet 在 Web 应用中的位置
浏览器 (HTTP Request) -> Web Server (Tomcat, Jetty) -> Servlet Container -> Servlet | Servlet (HTTP Response) <- Servlet Container <- Web Server <- 浏览器 (HTTP Response)
- Web 服务器: 处理网络连接。
- Servlet 容器: 管理生命周期,常见如 Tomcat、Jetty。
- Servlet: 业务逻辑单元。
Servlet API 核心
Servlet API 主要由两个包组成,Jakarta EE 9+ 后包名从 javax.servlet 迁移至 jakarta.servlet。
javax.servlet / jakarta.servlet 包
包含协议无关的核心接口,如 Servlet(生命周期)、ServletContext(应用范围共享)、ServletRequest 等。
javax.servlet.http / jakarta.servlet.http 包
提供 HTTP 特有接口,如 HttpServlet、HttpServletRequest、HttpServletResponse 和 HttpSession。
Servlet 生命周期
生命周期由容器管理,包含三个关键方法:
init(ServletConfig config)
实例创建后、首次请求前调用一次。用于一次性初始化,如读取配置或建立连接池。注意不要在此处直接创建数据库连接,建议使用连接池管理。
service(ServletRequest req, ServletResponse res)
每次请求到达时调用。容器会为每个请求创建新线程执行此方法,因此Servlet 必须是线程安全的,避免使用实例变量存储请求状态。
destroy()
应用卸载或服务器关闭时调用一次。用于清理资源,如关闭连接。
HttpServlet 详解
开发 HTTP Servlet 最常用的基类,继承自 GenericServlet。
核心方法
doGet: 处理 GET 请求,通常用于查询数据。doPost: 处理 POST 请求,通常用于提交数据。doPut,doDelete: 分别对应更新和删除操作。
实际开发中,我们继承 HttpServlet 并重写需要的 doXxx 方法即可。
处理 HTTP 请求参数
参数可通过 URL 查询字符串或请求体传递。
getParameter(String name): 获取单个值。getParameterValues(String name): 获取多值参数(如复选框)。getParameterMap(): 获取所有参数。
注意:处理非 ASCII 字符时,需在调用 getParameter 前设置编码,如 request.setCharacterEncoding("UTF-8")。
HttpServletRequest
封装了客户端发送的所有 HTTP 请求信息。
获取请求信息
getMethod(): 获取 HTTP 方法。getRequestURI(): 获取请求 URI。getHeader(String name): 获取请求头。getCookies(): 获取 Cookie 数组。
请求属性与分发
属性存储在 request 对象中,仅在当前请求范围内有效,常用于 Servlet 间传递数据。
- 转发 (forward): 完全交给目标资源,URL 不变。
- 包含 (include): 将目标资源输出包含在当前响应中。
RequestDispatcher dispatcher = request.getRequestDispatcher("/path/to/resource");
dispatcher.forward(request, response);
HttpServletResponse
用于生成发送给客户端的响应。
设置状态码与头信息
setStatus(int sc): 设置状态码。sendError(int sc, String msg): 发送错误。setContentType(String type): 设置内容类型,如text/html;charset=UTF-8。
获取输出流
getOutputStream(): 二进制数据。getWriter(): 文本数据。 两者不能同时调用。
重定向
response.sendRedirect("/newpage");
告诉浏览器发起新的请求,地址栏会改变。
会话管理 (HttpSession)
HTTP 是无状态的,Session 用于跟踪用户状态。
获取 Session
HttpSession session = request.getSession(); // 获取或创建
HttpSession session = request.getSession(false); // 仅获取,不存在则返回 null
存储与移除数据
setAttribute(name, value)getAttribute(name)removeAttribute(name)invalidate(): 使会话失效。
超时配置
可在代码中设置 session.setMaxInactiveInterval(秒数),或在 web.xml 中配置 <session-timeout>。
Servlet 配置
支持 web.xml 部署描述符和注解两种方式。
web.xml 配置
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
注解配置 (@WebServlet)
Servlet 3.0+ 引入,更简洁。
@WebServlet(urlPatterns = "/annotated", name = "AnnotationServlet")
public class AnnotationServlet extends HttpServlet { ... }
Servlet 上下文 (ServletContext)
代表整个 Web 应用,所有组件共享同一个对象。
共享数据
setAttribute,getAttribute: 应用范围共享。getInitParameter: 获取应用初始化参数。getResourceAsStream: 读取 WEB-INF 下的资源。
Servlet 过滤器 (Filter)
拦截请求和响应,用于日志、安全、编码转换等横切关注点。
生命周期
init: 初始化。doFilter: 核心逻辑,必须调用chain.doFilter()继续链式处理。destroy: 销毁。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 前置处理
chain.doFilter(request, response);
// 后置处理
}
Servlet 监听器 (Listener)
监听 Web 应用事件,如 Context 创建、Session 销毁等。
常用接口
ServletContextListener: 监听应用启动/关闭。HttpSessionListener: 监听会话创建/销毁。
异步 Servlet
传统模型阻塞线程,高并发下性能受限。Servlet 3.0+ 支持异步处理。
基本模式
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
AsyncContext asyncContext = request.startAsync();
executor.submit(() -> {
try {
Thread.sleep(5000);
HttpServletResponse asyncResponse = (HttpServletResponse) asyncContext.getResponse();
asyncResponse.getWriter().write("Async Response");
} finally {
asyncContext.complete();
}
});
}
}
适用于长轮询、长时间计算等场景。
Servlet 与 Spring Boot
Spring Boot 底层仍运行在 Servlet 容器上,但简化了配置。
嵌入式容器
默认集成 Tomcat,无需单独部署 WAR。
注册组件
- 方式一: 使用
@ServletComponentScan+@WebServlet等注解。 - 方式二: 使用
@Bean+ServletRegistrationBean,灵活性更高。
访问 Servlet API
在 Controller 中可直接注入 HttpServletRequest 等对象。
最佳实践
- 线程安全: 避免使用实例变量存储请求状态,多用局部变量。
- 资源管理: 使用 try-with-resources 自动关闭流和连接。
- 异常处理: 捕获预期异常,未捕获异常由容器处理,建议配置错误页面。
- 性能优化: 避免不必要的同步,使用连接池和缓存。
- 安全考虑: 防止 XSS(输出编码)、SQL 注入(预编译)、CSRF(Token 验证),敏感数据加密传输。
实战案例
案例一:基础用户登录与会话管理
演示如何验证凭据、创建 Session 及保护受保护页面。
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private Map<String, String> validUsers = new HashMap<>();
{
validUsers.put("admin", "admin123");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if (validUsers.containsKey(username) && validUsers.get(username).equals(password)) {
HttpSession session = request.getSession();
session.setAttribute("user", username);
response.sendRedirect("welcome");
} else {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<h2>登录失败!</h2><a href='login'>返回</a>");
}
}
}
案例二:文件上传 Servlet
利用 @MultipartConfig 处理 multipart/form-data。
@WebServlet("/upload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part filePart = request.getPart("file");
String fileName = getSubmittedFileName(filePart);
String filePath = getServletContext().getRealPath("/") + "uploads/" + fileName;
filePart.write(filePath);
response.getWriter().println("上传成功!");
}
}
案例三:异步 Servlet 实现简单消息推送
模拟长轮询,延迟 5 秒后返回响应。
案例四:Spring Boot 中自定义 Filter 实现请求日志
记录 URL、IP 及耗时。
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LoggingFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
掌握这些核心概念与实战技巧,能让你在面对复杂的 Java Web 需求时更加游刃有余。无论是维护遗留系统还是构建现代微服务,Servlet 都是理解 Java Web 生态不可或缺的一环。

