Servlet 详解

目录


一、引言

1.1 Servlet 技术发展史

Servlet(Server Applet,服务器小程序)是 Java 平台上用于构建动态 Web 应用的服务器端组件规范。其发展历程反映了 Java 企业级 Web 技术从无到有、从规范到生态的演进过程。

版本 发布时间 关键特性
Servlet 1.0 1997 年 基础 CGI 替代方案,CGI 模式的同步处理
Servlet 2.2 1999 年 引入 WAR 文件、独立 Web 应用模型
Servlet 2.5 2005 年 注解支持简化部署描述
Servlet 3.0 2009 年 注解配置、异步处理、可插拔特性
Servlet 3.1 2013 年 非阻塞 IO 升级、HTTP 协议升级
Servlet 4.0 2017 年 HTTP/2 支持、服务器推送
Servlet 5.0 2020 年 Jakarta EE 迁移、命名空间切换

由上表可见,Servlet 规范历经二十余年演进,已具备现代 Web 服务所需的全部基础能力。

1.2 Servlet 在 Java EE 体系中的定位

Servlet 是 Java Enterprise Edition(Java EE,现为 Jakarta EE)体系中最早成熟的 Web 层规范,位于整个企业级 Java 架构的核心层级。

flowchart TB A[客户端浏览器] --> B[Web 容器层 Servlet] B --> C[业务服务层 EJB 或 Spring Bean] C --> D[数据访问层 JPA 或 JDBC] D --> E[数据库存储] C --> D B --> C

容器层作为请求入口与业务逻辑之间的桥梁,承担请求分发、协议解析、会话管理、安全控制等横切关注点。

1.3 学习 Servlet 的意义与价值

在 Spring、Spring Boot 等高级框架高度流行的当下,直接学习 Servlet 仍具备不可替代的价值:

  1. 理解底层原理:所有 Java Web 框架均基于 Servlet 构建,掌握底层有助于理解框架封装逻辑。
  2. 排查疑难问题:内存泄漏、线程安全、连接耗尽等问题,需要深入 Servlet 容器行为才能定位。
  3. 定制 Web 容器行为:过滤器、监听器、异步处理等高级特性是任何框架无法完全替代的能力。
  4. 维护遗留系统:金融、电信、政企领域的核心系统仍广泛运行 Servlet 规范的应用。

二、核心概念

2.1 Servlet 定义与生命周期

Servlet 是运行在 Web 服务器或应用服务器上的 Java 类,用于接收并响应来自 Web 客户端的请求。其生命周期由 Servlet 容器管理,包含加载类、实例化、初始化、服务、销毁五个阶段。

stateDiagram-v2 [*] --> 加载类 加载类 --> 实例化 实例化 --> 初始化 初始化 --> 服务 服务 --> 服务 服务 --> 销毁 销毁 --> [*]
  • 加载类:容器根据配置加载 Servlet 类字节码。
  • 实例化:容器创建 Servlet 实例,仅执行一次。
  • 初始化:调用 init 方法,完成资源准备。
  • 服务:调用 service 方法,处理每一个客户端请求,重复执行。
  • 销毁:调用 destroy 方法,释放资源,仅执行一次。

2.2 Servlet 容器

Servlet 容器(Servlet Container)是实现 Servlet 规范的 Web 服务器组件,负责管理 Servlet 生命周期并提供请求路由、协议解析、安全控制等基础服务。

主流容器对照如下:

容器 维护方 特点
Apache Tomcat Apache 软件基金会 轻量级、生态成熟
Jetty Eclipse 基金会 高性能、嵌入式友好
Undertow Red Hat 非阻塞 IO、低延迟
WildFly Red Hat 全栈 Java EE 服务
flowchart LR Client[客户端] -->|HTTP 请求| Container[Servlet 容器] Container --> Parser[协议解析] Container --> Session[会话管理] Container --> Lifecycle[生命周期管理] Container -->|HTTP 响应| Client

2.3 HTTP 请求与响应模型

Servlet 基于 HTTP 协议实现客户端与服务器的交互。一次完整的交互包含请求消息与响应消息两个方向的数据流。

请求消息结构:

  • 请求行:方法、URI、协议版本
  • 请求头:键值对形式的元信息
  • 空行:分隔头部与主体
  • 消息体:表单数据或 JSON 等负载

响应消息结构:

  • 状态行:协议版本、状态码、原因短语
  • 响应头:服务器信息、内容类型等
  • 空行:分隔头部与主体
  • 消息体:HTML、JSON、二进制等响应内容
方法 语义 幂等性
GET 获取资源 是
POST 提交数据 否
PUT 更新资源 是
DELETE 删除资源 是

2.4 Servlet API 主要接口

Servlet API 由 javax.servlet 与 javax.servlet.http 两个核心包组成,关键接口如下:

classDiagram class Servlet { <<interface>> +init() +service() +destroy() +getServletConfig() } class GenericServlet { <<abstract>> +service() } class HttpServlet { <<abstract>> +doGet() +doPost() +doPut() +doDelete() } class ServletRequest { <<interface>> +getParameter() +getAttribute() } class ServletResponse { <<interface>> +getWriter() +setContentType() } Servlet <|-- GenericServlet GenericServlet <|-- HttpServlet HttpServlet --> ServletRequest HttpServlet --> ServletResponse

实际开发中最常使用的接口组合:

  • HttpServlet:HTTP 协议专用基类
  • HttpServletRequest:封装 HTTP 请求
  • HttpServletResponse:封装 HTTP 响应
  • ServletConfig:初始化配置
  • ServletContext:全局上下文

三、详细说明

3.1 Servlet 生命周期详解

Servlet 生命周期由容器严格管控。理解各阶段的执行时机与异常处理策略,是开发高可靠 Web 应用的基础。

生命周期关键方法:

  1. 构造器:仅在实例化阶段执行一次。
  2. init(ServletConfig):完成数据库连接、缓存预热等初始化操作。
  3. service(ServletRequest, ServletResponse):每个请求调用一次。
  4. destroy():容器关闭或重新部署时调用,用于释放文件句柄、关闭连接池。
  5. <load-on-startup>:通过 web.xml 配置项可控制 Servlet 在容器启动时即初始化。
sequenceDiagram participant Container as Servlet 容器 participant Servlet as Servlet 实例 participant Client as 客户端 Container->>Servlet: 加载类与实例化 Container->>Servlet: 调用 init 方法 Servlet-->>Container: 初始化完成 Client->>Container: 发送 HTTP 请求 Container->>Servlet: 调用 service 方法 Servlet->>Servlet: 根据方法分派 doGet 或 doPost Servlet-->>Container: 返回响应 Container-->>Client: 响应客户端 Container->>Servlet: 调用 destroy 方法 Servlet-->>Container: 销毁完成

3.2 请求处理机制

Servlet 接收到请求后,由 service 方法根据 HTTP 方法类型分派到对应的 doXxx 方法。开发者通常重写 doGet、doPost 等方法实现业务逻辑。

请求处理流程:

flowchart TD Start[接收 HTTP 请求] --> Parse[解析请求行与请求头] Parse --> Wrapper[封装 HttpServletRequest] Wrapper --> Dispatch[分派到 service 方法] Dispatch --> Method{判断 HTTP 方法} Method -->|GET| DoGet[doGet 方法] Method -->|POST| DoPost[doPost 方法] Method -->|PUT| DoPut[doPut 方法] Method -->|DELETE| DoDelete[doDelete 方法] DoGet --> Build[构建 HttpServletResponse] DoPost --> Build DoPut --> Build DoDelete --> Build Build --> Send[发送响应] Send --> End[结束]

请求处理中的关键对象:

  • 请求参数:通过 getParameter 获取查询字符串或表单字段。
  • 请求属性:通过 setAttribute 与 getAttribute 在组件间传递数据。
  • 请求头:通过 getHeader 获取 Cookie、User-Agent 等元信息。
  • 响应输出:通过 getWriter 或 getOutputStream 写入响应内容。
  • 中文编码:通过 setCharacterEncoding 与 setContentType 协同处理。

3.3 会话管理

HTTP 协议本身是无状态的,会话管理用于在多次请求间保持用户状态。Servlet 规范提供四种会话跟踪机制。

机制 实现原理 优点 缺点
Cookie 客户端存储小段文本 实现简单、可禁用 可被篡改、大小受限
HttpSession 服务端维护会话对象 安全可靠 占用服务端内存
URL 重写 将会话 ID 拼接到 URL 兼容禁用 Cookie 场景 URL 冗长、外泄风险
隐藏表单域 通过表单隐藏字段传递 简单可控 仅适用于表单提交
sequenceDiagram participant Browser as 浏览器 participant Servlet as Servlet participant SessionStore as 会话存储 Browser->>Servlet: 首次请求 携带 Cookie 或 URL 中的 Session ID Servlet->>SessionStore: 创建或查询 HttpSession SessionStore-->>Servlet: 返回 Session 对象 Servlet->>Browser: 设置 Set-Cookie 响应头 或 写入 URL Browser->>Servlet: 后续请求携带 Session ID Servlet->>SessionStore: 查找现有 Session SessionStore-->>Servlet: 返回 Session 对象

HttpSession 常用方法:

  • setAttribute(String, Object):存储会话属性。
  • getAttribute(String):获取会话属性。
  • removeAttribute(String):移除会话属性。
  • invalidate():销毁当前会话。
  • setMaxInactiveInterval(int):设置超时时间,单位秒。

3.4 过滤器与监听器

过滤器(Filter)是 Servlet 规范定义的组件级拦截机制,可在请求到达 Servlet 之前或响应返回客户端之前执行预处理与后处理逻辑。

过滤器典型应用场景:

  • 字符编码统一设置
  • 用户身份认证与权限校验
  • 请求日志与审计
  • XSS、SQL 注入等安全过滤
  • 响应数据压缩与缓存
flowchart LR Request[客户端请求] --> F1[过滤器 1] F1 --> F2[过滤器 2] F2 --> F3[过滤器 N] F3 --> Servlet[目标 Servlet] Servlet --> F3 F3 --> F2 F2 --> F1 F1 --> Response[客户端响应]

监听器(Listener)用于监听 Web 应用生命周期事件与对象状态变化,主要接口包括:

  • ServletContextListener:监听应用启动与关闭。
  • HttpSessionListener:监听会话创建与销毁。
  • ServletRequestListener:监听请求创建与销毁。
  • HttpSessionAttributeListener:监听会话属性变更。

监听器与过滤器协同工作,可实现完整的横切关注点处理。

3.5 异步处理与 WebSocket

Servlet 3.0 起引入异步处理机制,允许 Servlet 在等待耗时操作时释放容器线程,提升高并发场景下的吞吐量。

异步处理关键 API:

  • AsyncContext:异步上下文对象。
  • request.startAsync():开启异步处理。
  • asyncContext.complete():标记异步处理完成。
  • asyncContext.dispatch():将响应分派到指定资源。
sequenceDiagram participant Client as 客户端 participant Container as Servlet 容器 participant Servlet as 异步 Servlet participant Worker as 工作线程 Client->>Container: 发起请求 Container->>Servlet: 分配容器线程 Servlet->>Container: 调用 startAsync 并释放容器线程 Container-->>Servlet: 返回容器线程给线程池 Servlet->>Worker: 提交任务至工作线程池 Worker->>Worker: 执行耗时操作 Worker->>Container: 通过 AsyncContext 提交响应 Container-->>Client: 返回响应

WebSocket(Servlet 3.1+ 支持)提供全双工通信能力,适用于实时聊天、行情推送、协同编辑等场景。Servlet 4.0 起新增 HTTP/2 服务器推送能力,可由服务端主动向客户端推送关联资源。


四、示例

4.1 Hello World 示例

最基础的 Servlet 实现,演示请求响应全流程。

Step 1:编写 Servlet 类

文件路径:src/main/java/com/example/HelloServlet.java

package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head><title>Hello Servlet</title></head>");
        out.println("<body>");
        out.println("<h1>欢迎学习 Servlet</h1>");
        out.println("<p>当前时间:" + new java.util.Date() + "</p>");
        out.println("</body>");
        out.println("</html>");
    }
}

Step 2:注册 Servlet

文件路径:src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         version="3.1">
    <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>
</web-app>

Step 3:访问验证

部署至 Tomcat 后访问 http://localhost:8080/hello,页面将显示欢迎信息与当前时间。

4.2 表单处理示例

演示登录表单的提交与参数读取。

Step 1:登录页面

文件路径:src/main/webapp/login.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
    <form action="login" method="post">
        <p>
            <label>用户名:</label>
            <input type="text" name="username" required>
        </p>
        <p>
            <label>密码:</label>
            <input type="password" name="password" required>
        </p>
        <p>
            <button type="submit">登录</button>
        </p>
    </form>
</body>
</html>

Step 2:处理 Servlet

文件路径:src/main/java/com/example/LoginServlet.java

package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        String username = request.getParameter("username");
        String password = request.getParameter("password");

        PrintWriter out = response.getWriter();
        if ("admin".equals(username) && "123456".equals(password)) {
            out.println("<h1>登录成功</h1>");
            out.println("<p>欢迎," + username + "</p>");
        } else {
            out.println("<h1>登录失败</h1>");
            out.println("<p>用户名或密码错误</p>");
        }
    }
}

4.3 数据库操作示例

演示通过 Servlet 查询数据库并渲染结果列表。

Step 1:数据访问对象

文件路径:src/main/java/com/example/UserDao.java

package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class UserDao {

    private static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
    private static final String USER = "root";
    private static final String PASSWORD = "root";

    public List<String> findAllUsernames() throws Exception {
        List<String> usernames = new ArrayList<>();
        Class.forName("com.mysql.jdbc.Driver");
        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
             PreparedStatement ps = conn.prepareStatement("SELECT username FROM users");
             ResultSet rs = ps.executeQuery()) {
            while (rs.next()) {
                usernames.add(rs.getString("username"));
            }
        }
        return usernames;
    }
}

Step 2:用户列表 Servlet

文件路径:src/main/java/com/example/UserListServlet.java

package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UserListServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        try {
            UserDao dao = new UserDao();
            List<String> usernames = dao.findAllUsernames();

            out.println("<h1>用户列表</h1>");
            out.println("<ul>");
            for (String name : usernames) {
                out.println("<li>" + name + "</li>");
            }
            out.println("</ul>");
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            out.println("<p>查询失败:" + e.getMessage() + "</p>");
        }
    }
}

4.4 过滤器示例

演示字符编码过滤器与日志过滤器的实现。

Step 1:字符编码过滤器

文件路径:src/main/java/com/example/EncodingFilter.java

package com.example;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class EncodingFilter implements Filter {

    private String encoding = "UTF-8";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String configured = filterConfig.getInitParameter("encoding");
        if (configured != null) {
            this.encoding = configured;
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

Step 2:日志过滤器

文件路径:src/main/java/com/example/LoggingFilter.java

package com.example;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        long startTime = System.currentTimeMillis();
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        System.out.println("[请求开始] " + httpRequest.getMethod() + " "
                + httpRequest.getRequestURI());

        chain.doFilter(request, response);

        long duration = System.currentTimeMillis() - startTime;
        System.out.println("[请求结束] " + httpRequest.getRequestURI()
                + " 耗时 " + duration + " 毫秒");
    }

    @Override
    public void destroy() {
    }
}

Step 3:过滤器配置

文件路径:src/main/webapp/WEB-INF/web.xml

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>com.example.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>loggingFilter</filter-name>
    <filter-class>com.example.LoggingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>loggingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4.5 会话管理示例

演示用户登录后通过会话保持身份,并实现访问受限资源。

Step 1:会话登录 Servlet

文件路径:src/main/java/com/example/SessionLoginServlet.java

package com.example;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionLoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        String username = request.getParameter("username");
        String password = request.getParameter("password");

        if ("admin".equals(username) && "123456".equals(password)) {
            HttpSession session = request.getSession();
            session.setAttribute("currentUser", username);
            session.setMaxInactiveInterval(1800);

            response.sendRedirect("dashboard");
        } else {
            response.getWriter().println("登录失败");
        }
    }
}

Step 2:会话校验过滤器

文件路径:src/main/java/com/example/AuthFilter.java

package com.example;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class AuthFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        HttpSession session = httpRequest.getSession(false);
        boolean loggedIn = session != null && session.getAttribute("currentUser") != null;

        boolean requiresAuth = httpRequest.getRequestURI().contains("/dashboard");

        if (requiresAuth && !loggedIn) {
            httpResponse.sendRedirect("login.html");
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
    }
}

Step 3:仪表板 Servlet

文件路径:src/main/java/com/example/DashboardServlet.java

package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class DashboardServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        HttpSession session = request.getSession(false);
        String currentUser = (String) session.getAttribute("currentUser");

        PrintWriter out = response.getWriter();
        out.println("<h1>仪表板</h1>");
        out.println("<p>当前登录用户:" + currentUser + "</p>");
        out.println("<p>会话 ID:" + session.getId() + "</p>");
    }
}

五、最佳实践

5.1 线程安全

Servlet 容器默认采用单实例多线程模型,每个 Servlet 类只有一个实例,由多个请求线程并发调用。这要求开发者严格遵守线程安全规范。

线程安全核心原则:

  1. 避免实例变量:所有请求级数据应通过方法参数、局部变量或 HttpServletRequest 属性传递。
  2. 谨慎使用共享对象:连接池、线程池等共享对象必须是线程安全的实现。
  3. 同步锁最小化:仅在必要时使用 synchronized,避免锁粒度过粗导致性能下降。
  4. 不可变对象优先:返回值与配置数据优先设计为不可变对象。
flowchart LR T1[请求线程 1] --> Servlet[Servlet 单例] T2[请求线程 2] --> Servlet T3[请求线程 3] --> Servlet Servlet --> Local[线程局部变量] Servlet --> Shared[共享只读对象] Servlet --> Danger[共享可变对象 风险] Danger -.警告.-> T1 Danger -.警告.-> T2 Danger -.警告.-> T3

线程安全常见误区:

  • 将用户数据存入 Servlet 实例字段。
  • 在 init 方法中维护请求级缓存。
  • 使用非线程安全的集合类(如 ArrayList、HashMap)作为共享状态。

5.2 性能优化

性能优化贯穿 Servlet 设计、编码、部署各阶段,关键措施包括:

连接与资源管理:

  • 使用数据库连接池(如 HikariCP、Druid)避免频繁创建连接。
  • 及时关闭 IO 流与数据库结果集。
  • 合理配置连接池参数,匹配业务并发量。

响应内容压缩:

  • 通过过滤器实现 Gzip 压缩。
  • 静态资源启用 HTTP 缓存。

线程与并发:

  • 耗时操作采用异步处理。
  • 关键业务利用线程池并发执行。

容器参数调优:

  • 调整 Tomcat 线程池最大线程数。
  • 合理设置 JVM 堆内存。
  • 启用 NIO 连接器。
flowchart TD A[性能瓶颈定位] --> B[线程分析] A --> C[内存分析] A --> D[数据库分析] B --> B1[线程池监控] B --> B2[锁竞争检测] C --> C1[堆转储分析] C --> C2[GC 日志分析] D --> D1[慢 SQL 捕获] D --> D2[连接池使用率]

5.3 安全防护

Servlet 应用直面客户端,安全防护至关重要,必须从认证、授权、输入校验、会话管理、传输加密五个层面建立纵深防御。

防护层次 措施 实现方式
认证 验证用户身份 表单登录、Token 验证、SSO 集成
授权 控制资源访问 基于角色、基于 URL、注解式
输入校验 过滤恶意输入 参数白名单、转义、预编译语句
会话安全 防止会话劫持 HTTPS、HttpOnly、Secure 标志
传输加密 防止窃听篡改 TLS 1.2+、HSTS、证书钉扎

常见 Web 攻击与防御:

  • 跨站脚本(XSS):对输出内容进行 HTML 转义,使用 OWASP Java Encoder。
  • 跨站请求伪造(CSRF):采用同步令牌或 SameSite Cookie 策略。
  • SQL 注入:使用 PreparedStatement,严格校验参数。
  • 会话固定:登录成功后调用 session.invalidate() 重建会话。
  • 不安全反序列化:避免反序列化不可信数据,使用白名单校验。
flowchart LR Client[客户端] -->|HTTPS| WAF[Web 应用防火墙] WAF --> AuthFilter[认证过滤器] AuthFilter --> AuthzFilter[授权过滤器] AuthzFilter --> XssFilter[XSS 过滤器] XssFilter --> Servlet[业务 Servlet] Servlet --> Dao[数据访问对象] Dao --> DB[(数据库)]

六、总结

6.1 核心要点回顾

本文系统讲解了 Servlet 技术体系,关键要点归纳如下:

  1. Servlet 是 Java Web 应用的底层组件规范,所有上层框架均构建于其上。
  2. Servlet 生命周期由容器管理,包含加载、实例化、初始化、服务、销毁五个阶段。
  3. 请求处理通过 service 方法分派至具体 doXxx 方法,需关注中文编码与异常处理。
  4. 会话管理依赖 HttpSession、Cookie、URL 重写等机制,需结合安全性综合考量。
  5. 过滤器与监听器提供横切关注点处理能力,是认证、日志、压缩等功能的标准实现。
  6. 异步处理、WebSocket、HTTP/2 等高级特性满足现代 Web 应用的多样化需求。
  7. 线程安全、性能优化、安全防护是 Servlet 应用开发必须长期关注的三项基础工程。

6.2 进阶学习路径

掌握基础概念后,可按以下路径继续深入:

flowchart LR A[Servlet 基础] --> B[Servlet 高级特性] B --> C[JSP 与 JSTL] B --> D[Servlet 异步与 WebSocket] B --> E[自定义 Servlet 容器] C --> F[MVC 框架设计] D --> F E --> F F --> G[Spring MVC 原理] F --> H[Spring Boot 内嵌容器] G --> I[响应式 Web 框架] H --> I I --> J[云原生 Web 服务]

推荐学习资源:

  • 官方规范:Jakarta Servlet 5.0 Specification。
  • 容器源码:Apache Tomcat 源码,重点阅读 Connector 与 Container 模块。
  • 优秀书籍:《Head First Servlet & JSP》、《Servlet 3.1 规范中文版》。
  • 实战项目:基于 Servlet 实现简易 MVC 框架,深入理解请求分发与视图解析。

6.3 现代 Web 开发的演进

Servlet 规范在演进,Web 开发模式也在持续变革。当前主流趋势如下:

技术趋势 核心思想 与 Servlet 的关系
注解驱动 通过注解替代 XML 配置 Servlet 3.0+ 已支持
嵌入式容器 应用与容器一体化打包 Spring Boot 内嵌 Tomcat
响应式编程 非阻塞、事件驱动 Servlet 3.1+ 提供非阻塞 IO
云原生 容器化部署、弹性伸缩 镜像化 Servlet 应用
微服务 单一职责、轻量通信 Servlet 提供 HTTP 基础能力

Servlet 作为 Java Web 技术体系的基石,既未被时代淘汰,也未停滞不前。理解其设计思想与运行机制,是掌握任何现代 Java Web 框架的必经之路。


文档结束

posted @ 2026-11-07 23:44  RK5123153  阅读(0)  评论(0)    收藏  举报