【Day33】Servlet 基础:生命周期、Request/Response 对象实战

本文收录于「Java 学习日记:从入门到架构师」专栏,聚焦 Java Web 核心组件 Servlet,从底层原理到实战落地,帮你彻底搞懂 Servlet 的工作方式~

一、为什么要学 Servlet?

在上一篇 Tomcat 的学习中,我们知道 Tomcat 是 Web 服务器,但它只是 "接收请求、返回响应" 的容器 ------ 真正处理业务逻辑(比如接收前端参数、操作数据库、返回数据)的核心组件,就是Servlet

简单来说:

  • Servlet 是 Java 编写的服务器端程序,是 Java Web 的 "基石";
  • SpringMVC 的 Controller 本质上是对 Servlet 的封装,懂 Servlet 才能理解 SpringMVC 的底层原理;
  • 所有 Java Web 项目的请求处理,最终都会落到 Servlet 层面。

今天这篇日记,我们从 Servlet 的核心概念、生命周期、Request/Response 对象实战入手,手把手带你写第一个 Servlet 程序。

二、Servlet 核心认知:什么是 Servlet?

Servlet(Server Applet)是运行在服务器端的 Java 小程序,遵循 Java Servlet 规范,核心作用是:

  • 接收客户端的 HTTP 请求(Request);
  • 处理请求(如参数解析、业务逻辑处理);
  • 生成 HTTP 响应(Response)返回给客户端。

Servlet 的核心特点:

  • 基于 Java 语言,跨平台;
  • 与 HTTP 协议深度绑定,能直接操作请求 / 响应的所有细节;
  • 由 Tomcat 等 Servlet 容器管理,无需手动创建和销毁。

三、Servlet 生命周期:从创建到销毁的全过程

Servlet 的生命周期完全由 Tomcat(Servlet 容器)管理,核心分为 4 个阶段,记住这个流程就能理解 Servlet 的运行原理:

1. 生命周期四阶段(核心)

(1)加载与实例化
  • 时机:默认情况下,Tomcat 第一次接收到访问该 Servlet 的请求时,加载 Servlet 类并创建唯一实例(Servlet 是单例的!);
  • 特点:整个应用生命周期内,一个 Servlet 类只创建一个实例,所有请求共享这个实例(注意线程安全问题)。
(2)初始化(init ())
  • 时机:实例化后立即执行,只执行一次
  • 作用:初始化 Servlet 的资源(如加载配置文件、创建数据库连接池);
  • 重写方式:public void init() throws ServletException {}
(3)处理请求(service ())
  • 时机:每次客户端发送请求时,Tomcat 会启动一个新线程调用 service () 方法;
  • 核心逻辑:service () 方法会根据请求方式(GET/POST),自动调用 doGet () 或 doPost () 方法;
  • 特点:每次请求都执行,是处理业务逻辑的核心。
(4)销毁(destroy ())
  • 时机:Tomcat 关闭、Servlet 被卸载时执行,只执行一次
  • 作用:释放 Servlet 占用的资源(如关闭数据库连接、释放文件句柄);
  • 重写方式:public void destroy() {}

2. 关键注意点

  • 单例多线程:Servlet 是单例的,多个请求共用一个实例,因此不要在 Servlet 中定义成员变量(会有线程安全问题);
  • 提前初始化 :若想让 Servlet 在 Tomcat 启动时就初始化(而非第一次请求时),可在 web.xml 中配置<load-on-startup>1</load-on-startup>(数字越小优先级越高)。

四、第一个 Servlet 程序:实战步骤

1. 开发环境准备

  • JDK 8+ + Tomcat 9 + IDEA(Eclipse 也可);
  • 新建 Java Web 项目(IDEA 中选择 "Java Enterprise",勾选 "Web Application")。

2. 编写 Servlet 类

创建FirstServlet.java,继承HttpServlet(核心父类),重写 doGet () 和 doPost () 方法:

java

运行

java 复制代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// 注解配置Servlet路径(替代web.xml),访问路径:http://localhost:8080/demo/first
@WebServlet("/first")
public class FirstServlet extends HttpServlet {

    // 初始化方法(可选重写)
    @Override
    public void init() throws ServletException {
        super.init();
        System.out.println("FirstServlet 初始化了!");
    }

    // 处理GET请求
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 设置响应编码(解决中文乱码)
        response.setContentType("text/html;charset=UTF-8");
        
        // 2. 获取PrintWriter,用于向客户端输出内容
        PrintWriter writer = response.getWriter();
        
        // 3. 输出响应内容
        writer.write("<h1>Hello Servlet!</h1>");
        writer.write("<p>Java学习日记 - Day33</p>");
        
        // 打印请求信息
        System.out.println("请求方法:" + request.getMethod());
        System.out.println("请求路径:" + request.getRequestURI());
    }

    // 处理POST请求(复用GET逻辑)
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    // 销毁方法(可选重写)
    @Override
    public void destroy() {
        super.destroy();
        System.out.println("FirstServlet 销毁了!");
    }
}

3. 配置与运行(两种方式)

方式 1:注解配置(推荐,Servlet 3.0 + 支持)

上面的代码中,@WebServlet("/first") 就是注解配置,直接指定 Servlet 的访问路径,无需修改 web.xml。

方式 2:web.xml 配置(传统方式)

若不用注解,可在WEB-INF/web.xml中添加配置:

xml

XML 复制代码
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 配置Servlet -->
    <servlet>
        <servlet-name>FirstServlet</servlet-name>
        <servlet-class>com.example.FirstServlet</servlet-class>
        <!-- 可选:启动时初始化,数字越小优先级越高 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 配置Servlet映射路径 -->
    <servlet-mapping>
        <servlet-name>FirstServlet</servlet-name>
        <url-pattern>/first</url-pattern>
    </servlet-mapping>
</web-app>

4. 部署与访问

  1. 将项目部署到 Tomcat(复制到 webapps 目录或 IDEA 中配置 Tomcat 运行);
  2. 启动 Tomcat;
  3. 访问 http://localhost:8080/demo/first(demo 是项目名),能看到页面输出 "Hello Servlet!" 即成功;
  4. 查看 Tomcat 控制台,能看到 "FirstServlet 初始化了!"(第一次访问时输出),每次刷新页面都会打印请求信息。

五、Request/Response 对象实战:核心 API

Servlet 的核心能力是处理 Request(请求)和 Response(响应),这两个对象由 Tomcat 创建并传入 doGet/doPost 方法,下面是高频使用的 API:

1. HttpServletRequest(请求对象)

作用:获取客户端的所有请求信息(参数、请求头、Cookie 等)。

高频 API:

java

运行

java 复制代码
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 1. 设置请求编码(解决POST参数中文乱码)
    request.setCharacterEncoding("UTF-8");
    
    // 2. 获取请求参数(GET/POST通用)
    String username = request.getParameter("username"); // 单个参数
    String[] hobbies = request.getParameterValues("hobby"); // 多个同名参数(如复选框)
    
    // 3. 获取请求头信息
    String userAgent = request.getHeader("User-Agent"); // 客户端类型
    String host = request.getHeader("Host"); // 主机名
    
    // 4. 获取请求路径相关信息
    String requestURI = request.getRequestURI(); // /demo/first
    String contextPath = request.getContextPath(); // /demo(项目名)
    
    // 5. 打印参数
    System.out.println("用户名:" + username);
    System.out.println("客户端类型:" + userAgent);
    
    // 响应内容
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter writer = response.getWriter();
    writer.write("用户名:" + username + "<br>");
    writer.write("请求路径:" + requestURI);
}

访问测试:http://localhost:8080/demo/first?username=Java日记&hobby=编程&hobby=学习,控制台和页面会显示对应的参数。

2. HttpServletResponse(响应对象)

作用:向客户端发送响应(页面、JSON、重定向等)。

高频 API:

java

运行

java 复制代码
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 1. 设置响应编码和数据格式
    response.setContentType("text/html;charset=UTF-8");
    // 或返回JSON:response.setContentType("application/json;charset=UTF-8");
    
    // 2. 向客户端输出内容
    PrintWriter writer = response.getWriter();
    writer.write("{\"code\":200,\"msg\":\"请求成功\"}"); // 输出JSON
    
    // 3. 重定向(跳转到其他页面)
    // response.sendRedirect("/demo/index.html");
    
    // 4. 设置响应状态码
    // response.setStatus(404); // 404未找到
    // response.sendError(500, "服务器内部错误"); // 500错误并提示信息
}

3. 解决中文乱码问题(必看)

新手最常踩的坑就是中文乱码,核心解决方案:

  • 请求乱码 :POST 请求需设置request.setCharacterEncoding("UTF-8");GET 请求乱码需修改 Tomcat 的conf/server.xml,添加URIEncoding="UTF-8"

    xml

    XML 复制代码
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8" />
  • 响应乱码 :必须先设置response.setContentType("text/html;charset=UTF-8"),再获取 PrintWriter。

六、Servlet 线程安全问题(重点避坑)

Servlet 是单例的,多个请求会共用同一个 Servlet 实例,因此不要在 Servlet 中定义成员变量,否则会出现线程安全问题:

❌ 错误示例(有线程安全问题):

java

运行

java 复制代码
@WebServlet("/unsafe")
public class UnsafeServlet extends HttpServlet {
    // 成员变量:多个线程共享,会被覆盖
    private String username;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        username = request.getParameter("username");
        // 模拟业务处理延迟
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        response.getWriter().write("用户名:" + username);
    }
}

✅ 正确示例(无线程安全问题):

java

运行

java 复制代码
@WebServlet("/safe")
public class SafeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 局部变量:每个线程独立,无安全问题
        String username = request.getParameter("username");
        response.getWriter().write("用户名:" + username);
    }
}

七、今日实战小任务

  1. 编写一个 Servlet,接收前端传入的 "姓名" 和 "年龄" 参数,在页面上输出 "你好,XXX!你的年龄是 XX 岁";
  2. 测试 GET 和 POST 请求的参数接收,解决中文乱码问题;
  3. 尝试用 response 实现重定向功能(跳转到百度首页)。

总结

  1. Servlet 是 Java Web 的核心组件,生命周期分为实例化、初始化(init)、处理请求(service/doGet/doPost)、销毁(destroy),其中 service 方法每次请求都会执行;
  2. HttpServletRequest 用于获取请求参数、请求头等信息,HttpServletResponse 用于向客户端返回响应,需注意设置编码解决中文乱码;
  3. Servlet 是单例多线程的,不要定义成员变量,避免线程安全问题,推荐使用注解@WebServlet配置访问路径。

下一篇【Day34】预告:Servlet 进阶:关注专栏持续解锁 Java Web 核心知识点~若本文对你有帮助,欢迎点赞 + 收藏 + 关注,你的支持是我更新的最大动力💖!

相关推荐
zfoo-framework27 分钟前
帧同步和状态同步
java
charlotte1024102430 分钟前
高并发:关于在等待学校教务系统选课时的碎碎念
java·运维·网络
亓才孓34 分钟前
[JDBC]PreparedStatement替代Statement
java·数据库
_F_y1 小时前
C++重点知识总结
java·jvm·c++
打工的小王1 小时前
Spring Boot(三)Spring Boot整合SpringMVC
java·spring boot·后端
毕设源码-赖学姐1 小时前
【开题答辩全过程】以 高校体育场馆管理系统为例,包含答辩的问题和答案
java·spring boot
我真会写代码1 小时前
SSM(指南一)---Maven项目管理从入门到精通|高质量实操指南
java·spring·tomcat·maven·ssm
vx_Biye_Design1 小时前
【关注可免费领取源码】房屋出租系统的设计与实现--毕设附源码40805
java·spring boot·spring·spring cloud·servlet·eclipse·课程设计
DN金猿1 小时前
接口路径正确,请求接口却提示404
java·tomcat
Maynor9962 小时前
OpenClaw 玩家必备:用 AI 自动追踪社区最新动态
java·服务器·人工智能