Servlet 快速入门

Servlet Quick Start

Posted by Marlin on November 3, 2025

Servlet 技术详解:JavaWeb 的核心控制器

一、Servlet 是什么?

定义与本质

Servlet 是运行在 Web 服务器或应用服务器上的 Java 程序,用于扩展服务器的功能,处理客户端(通常是浏览器)的请求并生成响应。其本质是一个Java 接口,定义了处理请求的标准方法。

关键特性

  • 平台无关性:基于 Java,可在任何支持 JVM 的服务器上运行
  • 持久性:Servlet 是单例的,加载后常驻内存,处理多个请求
  • 高效性:避免了 CGI 的进程创建开销,性能显著提升
  • 可伸缩性:可利用 Java 的多线程机制高效处理并发请求
  • 安全性:运行在服务器沙箱环境中,提供安全保障

二、Servlet 在 Web 架构中的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
客户端 (浏览器)
       ↓
HTTP 请求
       ↓
Web 服务器 (如 Apache/Nginx)
       ↓
Servlet 容器 (如 Tomcat)
       ├───→ Servlet (处理业务逻辑)
       ├───→ JSP (生成视图)
       └───→ 其他资源
       ↓
HTTP 响应
       ↓
客户端 (浏览器)
  • Servlet 容器(如 Tomcat 的 Catalina):负责管理 Servlet 生命周期、处理请求分发
  • 角色定位:在 MVC 架构中,Servlet 通常充当 Controller 角色

三、Servlet 工作原理

请求处理流程

  1. 客户端发起 HTTP 请求(GET/POST 等)
  2. Web 服务器接收请求,根据 URL 映射决定是否转发给 Servlet 容器
  3. Servlet 容器:
    • 查找对应的 Servlet 实例
    • 若不存在,加载并实例化 Servlet
    • 调用 service() 方法处理请求
  4. Servlet 处理请求:
    • 解析请求参数
    • 调用业务逻辑
    • 生成响应内容
  5. 容器将响应返回给 Web 服务器,最终返回给客户端

核心组件关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
                 +-----------------+
                 |   Web 服务器    |
                 | (Apache/Nginx)  |
                 +--------+--------+
                          |
                 +--------v--------+
                 |  Servlet 容器   |
                 |    (Tomcat)     |
                 +--------+--------+
                          |
          +---------------+---------------+
          |               |               |
+---------v------+ +------+-------+ +-----v--------+
|   HTTPServlet  | |  GenericServlet| | 其他Servlet  |
+----------------+ +--------------+ +-------------+
          |
+---------v------+
| 开发者自定义Servlet|
+----------------+

四、Servlet 生命周期

Servlet 的生命周期由容器管理,分为三个阶段:

1. 初始化阶段(Initialization)

  • 触发时机:Servlet 首次被请求时,或容器启动时(配置了 <load-on-startup>)
  • 关键方法:public void init(ServletConfig config) throws ServletException
  • 执行内容:
    • 加载配置参数
    • 初始化数据库连接池
    • 加载缓存数据
    • 一次性资源准备
  • 特点:整个生命周期中只执行一次

2. 服务阶段(Service)

  • 触发时机:每次接收到 HTTP 请求
  • 关键方法:public void service(ServletRequest req, ServletResponse res)
  • 执行流程:
    • 容器创建 HttpServletRequest 和 HttpServletResponse 对象
    • 根据 HTTP 方法(GET/POST/PUT/DELETE)调用相应方法
    • 在多线程环境下执行(线程不安全!)
  • 特点:可执行多次,是处理业务的核心阶段

3. 销毁阶段(Destruction)

  • 触发时机:容器关闭或应用卸载时
  • 关键方法:public void destroy()
  • 执行内容:
    • 释放数据库连接
    • 保存缓存数据
    • 释放其他资源
  • 特点:整个生命周期中只执行一次

五、核心接口与类

1. 核心接口

接口 说明
javax.servlet.Servlet 最顶层接口,定义生命周期方法
javax.servlet.ServletConfig 提供 Servlet 配置信息
javax.servlet.ServletContext 提供应用上下文信息
javax.servlet.ServletRequest 封装客户端请求信息
javax.servlet.ServletResponse 封装服务器响应信息
javax.servlet.http.HttpServlet 处理 HTTP 请求的抽象类

2. 常用实现类

类 说明 典型用途
HttpServlet 抽象类,实现 HTTP 协议 所有 Web 应用的基类
GenericServlet 通用协议 Servlet 非 HTTP 协议场景
HttpServletRequestWrapper 请求包装类 实现过滤、修改请求
HttpServletResponseWrapper 响应包装类 实现过滤、修改响应

六、核心方法详解

1. HTTP 方法处理

HttpServlet 为不同 HTTP 方法提供了专用方法:

1
2
3
4
protected void doGet(HttpServletRequest req, HttpServletResponse resp) // 处理 GET 请求
protected void doPost(HttpServletRequest req, HttpServletResponse resp) // 处理 POST 请求
protected void doPut(HttpServletRequest req, HttpServletResponse resp) // 处理 PUT 请求
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) // 处理 DELETE 请求

2. 关键方法功能

方法 功能 注意事项
init() 初始化资源 避免长时间操作,阻塞容器启动
service() 请求分发 通常不重写,由容器调用
doGet() 处理 GET 请求 参数在 URL 中,长度有限
doPost() 处理 POST 请求 适合表单提交,数据量大
destroy() 释放资源 确保所有资源被正确关闭

3. 请求/响应处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 获取请求参数
String username = request.getParameter("username");
String[] hobbies = request.getParameterValues("hobby");

// 设置响应内容类型
response.setContentType("text/html;charset=UTF-8");

// 获取输出流
PrintWriter out = response.getWriter();
out.println("<h1>Hello, " + username + "!</h1>");

// 重定向
response.sendRedirect("success.html");

// 请求转发
request.getRequestDispatcher("result.jsp").forward(request, response);

// 设置 Cookie
Cookie userCookie = new Cookie("username", username);
userCookie.setMaxAge(3600); // 1小时
response.addCookie(userCookie);

// 获取 Session
HttpSession session = request.getSession();
session.setAttribute("user", user);

七、Servlet 配置方式

1. 传统方式:web.xml 配置

1
2
3
4
5
6
7
8
9
10
<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com.example.LoginServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

2. 注解方式(Servlet 3.0+)

1
2
3
4
5
6
7
8
9
10
11
@WebServlet(
    name = "LoginServlet",
    urlPatterns = {"/login", "/signin"},
    loadOnStartup = 1,
    initParams = {
        @WebInitParam(name = "maxAttempts", value = "3")
    }
)
public class LoginServlet extends HttpServlet {
    // 实现代码
}

3. 编程式配置(Servlet 3.0+)

1
2
3
4
5
6
7
8
9
public class MyServletRegistration implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) {
        ServletRegistration.Dynamic reg = 
            ctx.addServlet("DynamicServlet", new DynamicServlet());
        reg.addMapping("/dynamic");
        reg.setLoadOnStartup(2);
    }
}

八、线程安全问题

问题根源

  • Servlet 实例是单例的(Singleton)
  • 多个请求共享同一个 Servlet 实例
  • 成员变量在多线程环境下可能产生数据不一致

解决方案

  1. 避免使用实例变量
    1
    2
    3
    4
    5
    6
    7
    
    // 不安全
    private String username; // 实例变量
       
    // 安全
    protected void doGet(HttpServletRequest req, HttpServletResponse res) {
        String username = req.getParameter("username"); // 局部变量
    }
    
  2. 使用同步机制
    1
    2
    3
    4
    5
    6
    7
    
    private final Object lock = new Object();
       
    protected void doPost(HttpServletRequest req, HttpServletResponse res) {
        synchronized(lock) {
            // 临界区代码
        }
    }
    
  3. 使用线程安全对象
    1
    
    private final ConcurrentHashMap<String, User> userCache = new ConcurrentHashMap<>();
    
  4. 使用 ThreadLocal
    1
    2
    
    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    

九、过滤器与监听器

1. 过滤器(Filter)

  • 作用:在请求到达 Servlet 前/后处理请求/响应
  • 应用场景:编码转换、权限检查、日志记录、压缩响应
  • 核心接口:javax.servlet.Filter
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    
    @WebFilter("/*")
    public class CharacterEncodingFilter implements Filter {
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
            req.setCharacterEncoding("UTF-8");
            res.setContentType("text/html;charset=UTF-8");
            chain.doFilter(req, res); // 传递给下一个过滤器或Servlet
        }
    }
    

2. 监听器(Listener)

  • 作用:监听 Web 应用中的关键事件
  • 常见类型:
    • ServletContextListener:监听应用启动/销毁
    • HttpSessionListener:监听会话创建/销毁
    • ServletRequestListener:监听请求开始/结束
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    @WebListener
    public class AppContextListener implements ServletContextListener {
        public void contextInitialized(ServletContextEvent sce) {
            // 应用启动时初始化数据库连接池
            DataSource ds = createDataSource();
            sce.getServletContext().setAttribute("dataSource", ds);
        }
          
        public void contextDestroyed(ServletContextEvent sce) {
            // 应用关闭时释放资源
            DataSource ds = (DataSource)sce.getServletContext().getAttribute("dataSource");
            if (ds != null) ds.close();
        }
    }
    

十、Servlet 3.0+ 新特性

1. 注解配置

  • 消除 web.xml 配置负担
  • @WebServlet, @WebFilter, @WebListener 等注解

2. 异步处理

  • 解决长轮询、实时通知等场景
  • 释放请求线程,提高容器吞吐量
  • 核心方法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    @WebServlet(urlPatterns = "/async", asyncSupported = true)
    public class AsyncServlet extends HttpServlet {
        protected void doGet(HttpServletRequest req, HttpServletResponse res) {
            AsyncContext asyncCtx = req.startAsync();
            asyncCtx.setTimeout(30000);
              
            new Thread(() -> {
                // 模拟耗时操作
                try { Thread.sleep(10000); } catch (Exception e) {}
                  
                // 完成异步处理
                asyncCtx.getResponse().getWriter().println("异步处理完成");
                asyncCtx.complete();
            }).start();
        }
    }
    

3. 文件上传 API

  • 简化文件上传处理
  • 内置对 multipart/form-data 的支持
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    @MultipartConfig
    public class FileUploadServlet extends HttpServlet {
        protected void doPost(HttpServletRequest req, HttpServletResponse res) {
            Part filePart = req.getPart("file");
            String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString();
              
            // 保存文件
            filePart.write("/uploads/" + fileName);
        }
    }
    

4. 动态注册

  • 运行时动态添加 Servlet、Filter、Listener
  • 适合插件化架构

十一、实战示例:用户注册功能

1. 创建 Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    private UserService userService;
    
    @Override
    public void init() throws ServletException {
        // 通常应该使用依赖注入,简化示例
        this.userService = new UserServiceImpl();
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        // 1. 设置编码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        
        // 2. 获取参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String email = request.getParameter("email");
        
        // 3. 验证输入
        if (username == null || username.trim().isEmpty()) {
            request.setAttribute("error", "用户名不能为空");
            request.getRequestDispatcher("/register.jsp").forward(request, response);
            return;
        }
        
        // 4. 处理业务
        try {
            User user = userService.register(username, password, email);
            
            // 5. 保存到会话
            HttpSession session = request.getSession();
            session.setAttribute("currentUser", user);
            
            // 6. 重定向到主页(防止表单重复提交)
            response.sendRedirect(request.getContextPath() + "/home");
            
        } catch (DuplicateUserException e) {
            request.setAttribute("error", "用户名已存在");
            request.getRequestDispatcher("/register.jsp").forward(request, response);
        } catch (Exception e) {
            request.setAttribute("error", "注册失败: " + e.getMessage());
            request.getRequestDispatcher("/register.jsp").forward(request, response);
        }
    }
}

2. web.xml 配置(如使用传统方式)

1
2
3
4
5
6
7
8
<servlet>
    <servlet-name>RegisterServlet</servlet-name>
    <servlet-class>com.example.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RegisterServlet</servlet-name>
    <url-pattern>/register</url-pattern>
</servlet-mapping>

十二、最佳实践

1. 关注点分离

  • Controller 层 (Servlet):只处理请求分发,不包含业务逻辑
  • Service 层:实现核心业务逻辑
  • DAO 层:负责数据访问
  • View 层 (JSP/Thymeleaf):只负责显示

2. RESTful 设计

  • 使用 HTTP 方法语义:GET(获取)、POST(创建)、PUT(更新)、DELETE(删除)
  • 设计有意义的 URL:/users, /users/123
  • 使用状态码表示结果:200(OK)、201(Created)、404(Not Found)、500(Internal Error)

3. 异常处理

  • 全局异常处理(使用过滤器或错误页面配置)
  • 避免向客户端暴露内部错误详情
  • 记录详细错误日志

4. 安全实践

  • 防止 XSS:对输出进行编码
  • 防止 CSRF:使用 token 验证
  • 防止 SQL 注入:使用预编译语句
  • 敏感操作验证权限

十三、现代 Web 开发中的定位

传统应用

  • 与 JSP 配合构建 MVC 架构
  • 适合中后台管理系统

现代应用

  • API 服务:作为 REST API 的控制器
  • 微服务:在 Spring Boot 等框架中作为基础组件
  • 前后端分离:提供 JSON 数据,前端使用 Vue/React 等框架

框架演进

  • Servlet → Spring Web MVC → Spring Boot → Spring WebFlux
  • 虽然框架在演进,但底层仍基于 Servlet API
  • 了解 Servlet 有助于理解上层框架的工作原理

总结

Servlet 作为 Java Web 开发的核心技术,是理解整个 Java Web 生态的基础。通过掌握其工作原理、生命周期和最佳实践,您将能够:

  1. 构建高性能、可维护的 Web 应用
  2. 理解上层框架(如 Spring MVC)的工作原理
  3. 解决复杂的 Web 开发问题
  4. 设计符合行业标准的架构模式

提示:在实际开发中,很少直接使用原始 Servlet API 构建应用,而是使用 Spring Boot 等框架简化开发。但深入理解 Servlet 对于解决问题、性能优化和架构设计至关重要。

建议学习路径:Servlet 基础 → Filter/Listener → JSP/EL/JSTL → JDBC 数据库连接 → MVC 架构 → Spring Boot