Servlet 全面解析(JavaWeb 核心)

Servlet 全面解析(JavaWeb 核心)

一、介绍

Servlet(Server Applet)是 JavaWeb 的核心组件,本质是运行在服务器端的 Java 类,用于接收和处理客户端(浏览器)的 HTTP 请求,生成并返回响应(HTML、JSON、重定向等)。它是连接 Java 后端与前端的桥梁,也是 SpringMVC 等框架的底层基础。

特性:
  1. 运行环境 :依赖 Web 服务器(Tomcat、Jetty 等),由服务器实例化和管理(而非手动 new)。
  2. 遵循协议:基于 HTTP 协议,处理 GET/POST/PUT/DELETE 等请求方法。
  3. 生命周期:由服务器控制,从创建到销毁全程自动化。
  4. 线程模型:默认多线程(每个请求对应一个线程),需注意线程安全问题。

二、Servlet 生命周期(核心)

Servlet 的生命周期完全由 Web 服务器(如 Tomcat)管理,分为 4 个阶段:

阶段 触发时机 核心方法 说明
初始化(初始化一次) 1. 服务器启动时;2. 首次访问 Servlet 时 init(ServletConfig) 只执行一次,用于初始化资源(如加载配置、连接数据库)
处理请求(多次) 每次客户端发送请求时 service(),doGet()/doPost() service() 自动判断请求方法,调用对应 doXxx();可重写 doGet/doPost 处理具体逻辑
销毁(销毁一次) 服务器关闭 / 项目卸载时 destroy() 只执行一次,用于释放资源(如关闭数据库连接、销毁线程)
获取配置信息 任意阶段 getServletConfig() 获取 Servlet 配置参数(如初始化参数、上下文对象)

生命周期示例代码

java 复制代码
@WebServlet("/lifeCycle")
public class LifeCycleServlet extends HttpServlet {
    // 1. 初始化:仅执行一次
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println("Servlet 初始化,加载资源...");
    }

    // 2. 处理 GET 请求(每次请求执行)
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("处理 GET 请求...");
        System.out.println("处理一次 GET 请求");
    }

    // 3. 销毁:仅执行一次
    @Override
    public void destroy() {
        super.destroy();
        System.out.println("Servlet 销毁,释放资源...");
    }
}
Servlet 生命周期流程图:



GET
POST
PUT/DELETE


Web服务器启动/首次访问Servlet
是否已创建Servlet实例?
创建Servlet实例
调用init初始化(仅执行一次)
接收客户端HTTP请求
调用service()方法,自动判断请求类型
请求方法
调用doGet()
调用doPost()
调用对应doXxx()
生成响应并返回客户端
服务器是否关闭/项目卸载?
调用destroy()销毁(仅执行一次)
释放资源(数据库连接/线程等)
Servlet实例被GC回收

三、Servlet 核心 API 与关键对象

1. 核心父类 / 接口
  • Servlet:顶级接口,定义生命周期方法(init()/service()/destroy())。
  • GenericServlet:实现 Servlet 接口,封装通用逻辑(如配置参数),抽象 service() 方法。
  • HttpServlet:继承 GenericServlet,专门处理 HTTP 请求,实现 service() 方法并拆分出 doGet()/doPost() 等方法(开发中直接继承此类)。
2. 三大核心对象(请求 / 响应 / 上下文)
对象 作用 核心方法
HttpServletRequest 封装客户端请求信息(请求行、请求头、请求参数) getParameter(String name):获取请求参数 getSession():获取 Session getHeader(String name):获取请求头
HttpServletResponse 封装服务器响应信息(响应行、响应头、响应体) getWriter():获取字符输出流(返回文本) setContentType():设置响应类型(如 JSON/HTML) sendRedirect():重定向
ServletContext 全局上下文对象(每个 Web 应用唯一),用于共享数据、获取全局配置 setAttribute(String key, Object value):设置全局属性 getAttribute(String key):获取全局属性 getRealPath(String path):获取文件绝对路径
Servlet 核心对象关系图

提供上下文
封装
封装
关联
输出
初始化参数
发送HTTP请求
返回响应
ServletContext 全局上下文对象(应用唯一)
HttpServlet 核心实现类
HttpServletRequest 请求对象(封装请求数据)
HttpServletResponse 响应对象(封装响应数据)
HttpSession 会话对象
客户端浏览器
ServletConfig 配置对象

区别:
维度 HttpServletRequest HttpServletResponse
作用 封装客户端请求数据 封装服务器响应数据
数据流向 客户端 → 服务器 服务器 → 客户端
核心操作 读取(获取参数、请求头) 写入(输出内容、设置响应头)
跳转方式 请求转发(服务器内部,地址栏不变) 重定向(客户端跳转,地址栏变化)

示例(获取参数 + 返回 JSON)

java 复制代码
@WebServlet("/user")
public class UserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 处理请求参数(解决中文乱码)
        req.setCharacterEncoding("UTF-8");
        String username = req.getParameter("username");
        String age = req.getParameter("age");

        // 2. 设置响应(返回 JSON)
        resp.setContentType("application/json;charset=UTF-8");
        String json = "{\"username\":\"" + username + "\",\"age\":\"" + age + "\"}";
        resp.getWriter().write(json);

        // 3. 操作 ServletContext(全局共享数据)
        ServletContext context = getServletContext();
        context.setAttribute("onlineCount", 100); // 设置全局属性
        System.out.println("当前在线人数:" + context.getAttribute("onlineCount"));
    }
}
Servlet 处理请求时序图:

ServletContext ServletConfig 自定义Servlet Web服务器(Tomcat) 客户端浏览器 ServletContext ServletConfig 自定义Servlet Web服务器(Tomcat) 客户端浏览器 alt [实例不存在] alt [GET请求] [POST请求- ] alt [服务器关闭] 发送HTTP请求(GET/POST) 检查实例是否存在 创建Servlet实例 获取初始化参数 传递参数 调用init()初始化 调用service()方法 判断请求方法(GET/POST) 调用doGet() 调用doPost() 获取全局上下文数据 生成响应数据(HTML/JSON) 返回HTTP响应 调用destroy()销毁 释放全局资源

四、Servlet 配置方式

Servlet 有两种配置方式:注解配置(主流) 和 XML 配置(传统)。

1. 注解配置(Servlet 3.0+ 支持,推荐)

通过 @WebServlet 注解直接标注类,无需修改 web.xml

java 复制代码
// 核心属性:urlPatterns(访问路径)、loadOnStartup(启动时加载)
@WebServlet(
    urlPatterns = {"/hello", "/hi"}, // 多个访问路径
    loadOnStartup = 1, // 服务器启动时初始化(默认-1:首次访问初始化)
    initParams = {@WebInitParam(name = "encoding", value = "UTF-8")} // 初始化参数
)
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取初始化参数
        String encoding = getServletConfig().getInitParameter("encoding");
        resp.setContentType("text/html;charset=" + encoding);
        resp.getWriter().write("Hello Servlet!");
    }
}
2. XML 配置(web.xml,传统方式)

WEB-INF/web.xml 中配置 Servlet:

xml 复制代码
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="4.0">
    <!-- 1. 注册 Servlet -->
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.example.HelloServlet</servlet-class>
        <!-- 初始化参数 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <!-- 启动时加载 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 2. 映射访问路径 -->
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

五、Servlet 常见应用场景

  1. 处理表单提交:接收前端表单的用户名、密码,完成登录 / 注册逻辑。
  2. 生成动态页面:根据数据库数据动态生成 HTML 页面(如商品列表)。
  3. 提供接口:返回 JSON 数据,供前端 AJAX 调用(RESTful 接口)。
  4. 文件上传 / 下载 :处理客户端文件上传(需配合 Part 接口)、返回文件下载响应。
  5. 权限拦截:结合 Filter 实现登录校验(如未登录则重定向到登录页)。

文件下载示例

java 复制代码
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 设置响应头(告诉浏览器下载文件)
        resp.setHeader("Content-Disposition", "attachment;filename=test.txt");
        // 2. 读取服务器文件并写入响应流
        String realPath = getServletContext().getRealPath("/files/test.txt");
        FileInputStream fis = new FileInputStream(realPath);
        ServletOutputStream os = resp.getOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        fis.close();
        os.close();
    }
}

六、Servlet 线程安全问题

问题根源:Servlet 是单例(服务器只创建一个实例),多请求共用同一个实例,若实例变量被修改会导致线程安全问题。

解决方案:

  1. 避免使用实例变量(改用局部变量,每个线程独立);
  2. 若必须使用实例变量,加锁(synchronized),但会降低并发性能;
  3. 使用 ThreadLocal 存储线程私有数据。

错误示例(线程不安全)

java 复制代码
@WebServlet("/unsafe")
public class UnsafeServlet extends HttpServlet {
    private int count = 0; // 实例变量:多线程共享,会被覆盖

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        count++; // 多请求同时修改,结果错误
        resp.getWriter().write("count: " + count);
    }
}

正确示例(局部变量)

java 复制代码
@WebServlet("/safe")
public class SafeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        int count = 0; // 局部变量:每个线程独立
        count++;
        resp.getWriter().write("count: " + count);
    }
}

七、Servlet 与 JSP、SpringMVC 的关系

  1. Servlet vs JSP:
    • JSP 本质是 Servlet(JSP 会被服务器编译为 .java Servlet 文件);
    • Servlet 适合纯逻辑处理,JSP 适合页面展示(已逐渐被前后端分离替代)。
  2. Servlet vs SpringMVC:
    • SpringMVC 是基于 Servlet 的封装,核心 DispatcherServlet 本质是一个 Servlet;
    • SpringMVC 简化了请求映射、参数绑定、视图解析等,开发效率更高。

八、注意事项

  1. 掌握底层:Servlet 是 JavaWeb 底层,理解其生命周期和核心对象,能更好掌握 SpringMVC;
  2. 解决乱码 :GET 请求乱码(Tomcat 配置 URIEncoding="UTF-8"),POST 请求乱码(req.setCharacterEncoding("UTF-8"));
  3. 路径问题 :访问路径分为绝对路径(/hello)和相对路径(hello),推荐使用绝对路径;
  4. 资源释放 :在 destroy() 中释放数据库连接、线程池等资源,避免内存泄漏。

Servlet 是 JavaWeb 的基石,虽然现在企业开发更多使用 SpringMVC,但理解 Servlet 的核心原理,能帮助你解决框架封装后的底层问题,是后端工程师的必备基础。

相关推荐
vx_bisheyuange2 小时前
基于SpringBoot的疗养院管理系统
java·spring boot·后端
创客匠人老蒋2 小时前
创客匠人“陪跑计划”:0前置费用助力知识IP从0到1突破
网络·创始人ip·创客匠人
京东零售技术2 小时前
2025京东零售技术年度精选 | 技术干货篇(内含福利)
前端·javascript·后端
晚枫歌F2 小时前
TCP协议详解
网络·网络协议·tcp/ip
村口曹大爷2 小时前
JDK 24 正式发布:性能压轴,为下一代 LTS 铺平道路
java·开发语言
布列瑟农的星空2 小时前
2025年度总结——认真生活,快乐工作
前端·后端
1.14(java)2 小时前
MySQL数据库操作全攻略
java·数据库·mysql
sunnyday04262 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
晴虹3 小时前
lecen:一个更好的开源可视化系统搭建项目--页面设计器(表单设计器)--全低代码|所见即所得|利用可视化设计器构建你的应用系统-做一个懂你的人
前端·后端·低代码