Servlet完全上手指南:核心语法与生命周期详解
- Servlet完全上手指南:核心语法与生命周期详解
-
- 一、引言
- 二、使用IDEA创建servlet
- 三、项目结构解析
-
- [1. 项目根目录文件](#1. 项目根目录文件)
- [2. src 目录](#2. src 目录)
-
- [(1)src/main 目录](#(1)src/main 目录)
- [(2)src/test 目录](#(2)src/test 目录)
- [3. target 目录](#3. target 目录)
- [4. 其他目录](#4. 其他目录)
- 三、Servlet与JSP协同运行的全链路流程
-
- [1. Servlet+JSP协同流程](#1. Servlet+JSP协同流程)
- [2. 客户端(浏览器)](#2. 客户端(浏览器))
- 三、javax到jakarta的不兼容变更
- [四、Servlet 生命周期](#四、Servlet 生命周期)
-
- [1. 什么是Servlet生命周期?](#1. 什么是Servlet生命周期?)
- [2. Servlet生命周期方法](#2. Servlet生命周期方法)
- [3. 流程图](#3. 流程图)
- [五、Servlet API](#五、Servlet API)
-
- [1. 接口定义的方法](#1. 接口定义的方法)
- [2. Servlet API重要的接口与类](#2. Servlet API重要的接口与类)
- [3. Servlet接口使用演示(生命周期)](#3. Servlet接口使用演示(生命周期))
- [4. 运行结果](#4. 运行结果)
Servlet完全上手指南:核心语法与生命周期详解
一、引言
你是否曾好奇,当你点击一个网页链接或提交一个表单时,背后是谁在接收请求、处理数据、并最终生成你看到的页面?在Java Web
开发的世界里,扮演这个核心角色的,正是Servlet------这位久经沙场的"老将"至今仍是整个Java EE
体系无可争议的基石。
尽管如今各种高级框架(如Spring MVC
)大行其道,但它们绝大多数依然构建在Servlet
规范之上。理解Servlet,就如同掌握了打开Java Web
开发大门的钥匙;它不仅能帮助你轻松上手传统项目,更能让你深刻理解现代框架的工作原理,做到知其然,亦知其所以然。
-
一个
Servlet
的"样貌"是怎样的? ------ 认识其核心语法与代码结构。 -
它是如何"出生"、"工作"乃至"退休"的? ------ 深入理解其至关重要的生命周期。
无论你是刚刚踏入Web开发的新手,还是希望巩固基础、厘清概念的经验开发者,这篇文章都将为你提供一个清晰而坚实的起点。让我们开始这段探索之旅,亲手揭开Servlet
的神秘面纱。
二、使用IDEA创建servlet
首先在新建项目里面选择 Jakarta EE ,模板选择 Web 应用程序,服务器选择tomcat,语言为Java,构造系统为Maven,接着点下一步

一般IDEA会自动给你选好Servlet,我们这里可以选择把Hibernate Validator勾上
接下来点击创建,如果JSP程序可以运行,那就说明配置好了


三、项目结构解析
这是一个基于 Maven 构建的 Java Web 项目,主要用于学习或演示 Servlet 的生命周期相关内容。下面对项目结构进行详细解析:
1. 项目根目录文件
.gitignore
:Git 版本控制的忽略文件,指定 Git 提交时应忽略的文件/目录,避免不必要文件纳入版本管理。mvnw
和mvnw.cmd
:Maven Wrapper 脚本文件,可在无全局 Maven 时,自动下载指定版本 Maven 构建项目,保证构建环境一致。pom.xml
:Maven 项目核心配置文件,配置项目坐标、依赖、构建插件等,Maven 据此管理项目构建与依赖。
2. src 目录
src
目录存放项目源代码和资源文件,分 main
和 test
子目录。
(1)src/main 目录
存放项目主要源代码和资源文件。
java
目录 :存放 Java 源代码。com.demo1.lifecycle
包 :Java 包结构,组织 Java 类,避免类名冲突。HelloServlet
类 :Servlet 类,用于演示 Servlet 生命周期(初始化、服务请求、销毁等阶段),继承HttpServlet
并重写相关方法(如init()
、doGet()
、doPost()
、destroy()
等)。
resources
目录:存放项目资源文件,如配置文件、国际化资源文件等(若有相关资源会放于此)。webapp
目录 :Java Web 项目的 Web 应用根目录,部署到 Web 服务器(如 Tomcat)后,目录下文件可直接访问。WEB-INF
目录 :Web 应用受保护目录,客户端无法直接访问,仅服务器端可访问。web.xml
:Web 应用部署描述符,配置 Servlet(将HelloServlet
映射到特定 URL 路径)、过滤器、监听器、欢迎页面、错误页面等 Web 应用相关配置。index.jsp
:JSP(Java Server Pages)页面,动态网页技术,可在 HTML 中嵌入 Java 代码,可能作为欢迎页面或与HelloServlet
交互的页面。
(2)src/test 目录
存放项目测试代码(如单元测试代码),保证代码正确性。
3. target 目录
Maven 构建项目时的输出目录,存放编译后的字节码文件、打包生成的 WAR 包、构建过程临时文件等。执行 Maven 的 compile
、package
等命令时,输出产物存于此目录。
4. 其他目录
.idea
目录:IntelliJ IDEA 集成开发环境的配置目录,包含项目设置、缓存、索引等信息,用于 IDEA 对项目的管理和开发支持。.mvn
目录 :与 Maven Wrapper 相关,包含 Maven Wrapper 的配置文件,支持mvnw
和mvnw.cmd
脚本运行。
综上,该项目结构是典型的 Maven 管理的 Java Web 项目结构,通过合理的目录划分和配置文件,方便进行 Servlet 相关功能的开发、构建和部署。
三、Servlet与JSP协同运行的全链路流程
1. Servlet+JSP协同流程
流程步骤 | 主体 | 核心动作 | 关键说明 |
---|---|---|---|
1 | 浏览器 | 发送请求 | 向服务器发起包含目标资源(如HTML文档、userlogin.jsp 等)的HTTP请求 |
2 | 服务器 | 判断请求类型 | 区分请求是静态(如HTML文档)还是动态(如JSP页面) |
3 | 服务器(静态请求时) | 返回HTML文档 | 直接将对应的HTML文档作为响应回传给浏览器 |
4 | Web容器(动态请求时,如Tomcat) | 创建Servlet对象 | 针对动态请求(如JSP),由Web容器实例化对应的Servlet类 |
5 | Web容器 | 分配线程,调用方法 | 为Servlet处理请求分配线程,调用doGet 或doPost 方法执行业务逻辑 |
6 | Servlet | 生成响应 | 处理请求后,生成包含结果的HTTP响应 |
7 | 服务器 | 返回响应 | 将Servlet生成的响应回传给浏览器 |
8 | 浏览器 | 解析并展示响应 | 解析响应内容(如HTML、动态生成的页面等),呈现给用户 |

2. 客户端(浏览器)
- 发送 HTTP 请求(目标为
Servlet
或JSP
,如/LoginServlet
或/index.jsp
)
Web 服务器(如Tomcat
)
↓ - 监听端口接收请求,判断请求目标类型
├→ 若目标是 "普通 Servlet
" → 【转发至 Servlet 容器】 → Servlet 容器(按映射规则定位对应的 Servlet 类)
└→ 若目标是 "JSP
" → 【触发内置 JSP 引擎(如 Tomcat 的 Jasper)】 → JSP 引擎(首次请求 / JSP 文件修改时,将 JSP 编译为 Java 源文件→再编译为字节码文件,该字节码本质是 Servlet 类) → 【编译完成后,将 Servlet 类交给 Servlet 容器】 → Servlet 容器(复用编译后的 JSP Servlet 类)
↓ - Servlet 容器对 Servlet 进行初始化 (仅首次请求时执行:创建 Servlet 实例,调用
init()
方法加载配置 / 资源;非首次请求直接复用已有实例
Servlet 实例(普通 Servlet 实例 或 编译后的 JSP Servlet 实例)
↓ - 执行核心处理逻辑
├→ 若为 "普通 Servlet":调用service()
方法,按请求类型分发至doGet()/doPost()
,执行业务逻辑(如登录验证、数据查询)
└→ 若为 "JSP Servlet
":准备页面渲染相关逻辑
↓ - 判断是否需要普通 Servlet 向 JSP 传递数据
├→ 是(如登录成功后需展示用户信息):普通 Servlet 通过request.setAttribute()
存储数据 → 调用request.getRequestDispatcher()
转发至目标 JSP → 【JSP 对应的 Servlet 实例】
└→ 否(如直接访问 JSP 页面):直接进入【JSP 对应的 Servlet 实例】
↓ - JSP 对应的 Servlet 实例执行渲染:通过 EL 表达式(${})/JSTL 标签从 Request 中获取数据,拼接动态 HTML 内容
↓ - 生成响应:普通 Servlet 或 JSP Servlet 通过
ServletResponse
封装 HTTP 响应报文(含响应行、响应头、响应体)
↓ - Web 服务器接收响应报文,通过网络回传至客户端(浏览器)
↓ - 浏览器解析响应中的 HTML 内容(含动态数据),渲染并展示最终页面
三、javax到jakarta的不兼容变更
从 Tomcat 10 开始,Servlet API 的包命名空间发生了重大且不兼容的变更。
具体体现在:
-
命名空间改变:由旧的 javax.servlet.*全部改为 jakarta.servlet.*。
-
规范变更:此变更是为了支持 Jakarta EE 9 及更高版本规范(如提及的 Servlet 6.0)。
-
直接后果:这个变化导致了向下不兼容。所有使用旧版 javax包开发的 Servlet 应用程序无法直接部署和运行在 Tomcat 10 及以后的版本中。
简单来说:如果您要将一个老项目升级到 Tomcat 10+,通常需要修改代码中的所有相关导入(import)语句和配置文件中的命名空间声明,将 javax.servlet替换为 jakarta.servlet。这是一个升级过程中必须注意的关键迁移步骤。
四、Servlet 生命周期
1. 什么是Servlet生命周期?
Servlet生命周期指的是一个Servlet实例从被创建到最终被垃圾回收的整个过程。这个过程完全由Servlet容器(如Tomcat、Jetty等)管理,包括以下几个关键阶段:
加载类 → 实例化 → init() → service() → doGet()/doPost() → destroy() → 垃圾回收
- Servlet 初始化后调用 init () 方法。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 销毁前调用 destroy() 方法。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
2. Servlet生命周期方法
1. init () 方法
java
public void init() throws ServletException {
// 初始化代码...
}
-
功能说明:
init()
是 Servlet 的初始化方法,在 Servlet 实例被创建后仅执行一次。 -
主要用途:
执行一次性初始化操作,如加载配置文件、建立数据库连接池、初始化缓存等
可以通过重写此方法完成 Servlet 启动时的准备工作
-
特点:
若初始化失败,会抛出 ServletException,导致 Servlet 无法正常提供服务
可通过 web.xml 中的 配置,使 Servlet 在 Web 应用启动时就完成初始化
2. service () 方法
java
public void service(ServletRequest request,ServletResponse response)
throws ServletException, IOException{
}
-
功能说明:
service()
是处理客户端请求的核心方法,每次客户端发送请求时都会被调用。 -
主要用途:
接收客户端请求(封装在 ServletRequest 对象中)
处理请求并生成响应(通过 ServletResponse 对象返回)
-
特点:
会自动根据 HTTP 请求方法(GET、POST、PUT 等)调用对应的处理方法(如 doGet()、doPost())
需要处理两种异常:ServletException(Servlet 处理异常)和 IOException(输入输出异常)
通常不需要重写此方法,而是重写具体的请求处理方法
3. 请求处理方法
doGet ()
方法
java
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码
}
-
功能说明:
专门用于处理 HTTP GET 类型的请求。
-
适用场景:
从服务器获取资源(如查询数据、页面跳转)
请求参数会附加在 URL 后面,有长度限制
不适合传输敏感信息(参数可见)
-
典型应用:
搜索查询、分页展示、资源下载等
doPost ()
方法
java
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码
}
-
功能说明:
专门用于处理 HTTP POST 类型的请求。
-
适用场景:
向服务器提交数据(如表单提交、数据新增 / 修改)
请求参数在请求体中,无明显长度限制
相对安全,适合传输敏感信息(如用户名密码)
-
典型应用:
用户注册、登录验证、表单提交等
4. destroy () 方法
java
public void destroy() {
// 终止化代码...
}
功能说明:
destroy()
是 Servlet 生命周期的最后一个方法,在 Servlet 被销毁前仅执行一次。
主要用途:
- 释放资源,如关闭数据库连接、清理临时文件
- 保存 Servlet 的状态信息
触发时机:
- Web 应用被停止时
- Servlet 容器(如 Tomcat)关闭时
- Servlet 被从容器中移除时
3. 流程图

五、Servlet API
1. 接口定义的方法
Servlet
接口是 Jakarta Servlet
规范的核心,定义了所有 Servlet
组件必须遵循的基本方法和生命周期规范。任何 Servlet 类(包括 HttpServlet
)都直接或间接实现了这个接口,它是 Servlet 容器
与 Servlet
组件通信的桥梁。
java
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
2. Servlet API重要的接口与类
①根基:顶层接口
Servlet
接口:所有Servlet的根本契约。它定义了Servlet的生命周期方法:init()
, service()
, destroy()
。任何Servlet都必须直接或间接实现它。
ServletConfig
接口:代表一个Servlet的个体配置。提供了获取初始化参数和至关重要的 ServletContext
(应用上下文)的方法。
②中枢:通用实现类
GenericServlet
抽象类:
关系:它实现了 Servlet
接口和 ServletConfig
接口。
职责:它是协议无关的通用实现。它为上述接口的大部分方法提供了默认实现,起到了适配器的作用,简化了开发。
③核心:HTTP专属实现类
HttpServlet
抽象类:
关系:它继承自 GenericServlet
抽象类。
职责:它是专门为 HTTP 协议定制的核心抽象类,也是开发者最常直接继承的类。其核心功能是重写了 service方法,根据HTTP请求方法(GET, POST等)将请求分发给对应的 doGet()
, doPost()
等方法。
④工具:请求与响应对象
ServletRequest/ ServletResponse(接口)
:定义了通用的、协议无关的请求和响应功能。
HttpServletRequest/ HttpServletResponse
(接口):
关系:它们分别扩展(即继承)了 ServletRequest
和 ServletResponse
接口。
职责:在通用功能之上,增加了HTTP协议特有的方法(如获取请求头、Cookie、Session,设置状态码、重定向等)。
协作关系:
HttpServlet
处理 HttpServletRequest
对象(从中获取客户端请求信息)。
HttpServlet
创建并操作 HttpServletResponse
对象(向其写入要返回给客户端的数据和信息)。
由此可见,只要是 Servlet 类,就有自己由容器管理的、规范的生命周期。
其核心在于顶层的 Servlet接口,它就像一份"规定",明确规定了所有 Servlet 都必须具备的三个核心生命周期方法:
-
init(): 初始化方法,在 Servlet 被创建后立即调用,用于执行一次性性的设置工作。
-
service(): 核心服务方法,每次客户端请求到来时都会被调用,用于处理业务逻辑。
-
destroy(): 销毁方法,在 Servlet 被容器移除之前调用,用于释放资源和进行清理工作。
图中 GenericServlet
抽象类和 HttpServlet
抽象类都直接或间接地实现了 Servlet接口,这意味着它们"签署"了这份规定,承诺会拥有这些生命周期方法。
GenericServlet 提供了这些生命周期方法的基础实现。
HttpServlet 继承了 GenericServlet,并对其中的 service()方法进行了重写和扩展,加入了根据 HTTP 请求方法(GET, POST等)自动分发给 doGet(), doPost()等方法的逻辑。但这并未改变其生命周期的本质,只是让处理请求的环节变得更精细。
因此,结论是:
无论是一个最简单的直接实现 Servlet接口的类,还是最常用的继承 HttpServlet的类,它们都具备由 Web 容器(如 Tomcat)控制的、完整的生命周期(初始化、服务、销毁)。图中的所有类,正因为它们都是 Servlet接口谱系中的一员,所以都遵循这一规则。
3. Servlet接口使用演示(生命周期)
我们了解了Servlet的生命周期的原理之后,就可以自己来写一个Servlet的类了
这里我创建了一个新项目,包名为
java
package com.demo1.lifecycle;
以下是代码片段,展示了Servlet完整的生命周期
java
package com.demo1.lifecycle;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// 配置Servlet并设置启动时加载(值越小优先级越高)
@WebServlet(
urlPatterns = "/HelloWorld",
loadOnStartup = 1 // 容器启动时就初始化,而不是首次请求时
)
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private String initMessage; // 初始化参数示例
// 1. 构造方法(容器通过反射调用,通常不重写)
public HelloServlet() { // 修正:构造方法不能有void返回值
System.out.println("[Lifecycle] 1. Constructor called - Servlet instance created");
}
// 2. 初始化方法(仅执行一次)
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 从配置中获取初始化参数(可在web.xml中配置)
initMessage = config.getInitParameter("welcomeMessage");
System.out.println("[Lifecycle] 2. init() method called - Initialization completed");
System.out.println(" Initialization parameter value: " + initMessage);
}
// 3. 服务方法(每次请求都会执行,负责分发请求)
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("[Lifecycle] 3. service() method called - Start processing request");
// 调用父类的service方法,自动分发到doGet/doPost
super.service(request, response);
}
// 处理GET请求(实际业务逻辑)
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("[Lifecycle] 4. doGet() method called - Processing GET request");
// 设置响应内容类型和编码
response.setContentType("text/html;charset=UTF-8");
// 获取输出流并输出内容
try (PrintWriter out = response.getWriter()) {
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet Lifecycle Demo</title>"); // 页面标题改为英文
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello, World! (Jakarta EE)</h1>"); // 原内容保留(已为英文)
out.println("<p>Initialization Info: " + initMessage + "</p>"); // 初始化信息改为英文
out.println("<p>Current Thread: " + Thread.currentThread().getName() + "</p>"); // 当前线程改为英文
out.println("</body>");
out.println("</html>");
}
}
// 5. 销毁方法(仅执行一次)
@Override
public void destroy() {
System.out.println("[Lifecycle] 5. destroy() method called - Releasing resources");
// 这里可以释放数据库连接、关闭文件等资源
initMessage = null; // 清空资源示例
}
}
① 类定义与注解配置
java
@WebServlet(
urlPatterns = "/HelloWorld",
loadOnStartup = 1 // 容器启动时就初始化,而不是首次请求时
)
public class HelloServlet extends HttpServlet { ... }
- @WebServlet注解:替代传统的 web.xml 配置,声明这是一个 Servlet
- urlPatterns = "/HelloWorld":指定访问路径,通过http://域名/项目名/HelloWorld可访问
- loadOnStartup = 1:设置 Servlet 在容器启动时初始化(值为非负数),数值越小优先级越高
- 继承HttpServlet:这是所有处理 HTTP 请求的 Servlet 的基类
② Servlet 生命周期方法解析
(1)构造方法
java
public HelloServlet() {
System.out.println("[Lifecycle] 1. Constructor called - Servlet instance created");
}
- 由 Servlet 容器通过反射调用,创建 Servlet 实例
- 仅在 Servlet 第一次被加载时执行一次
- 注意:构造方法不能有返回值,代码中已修正了可能的错误
(2)初始化方法
java
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
initMessage = config.getInitParameter("welcomeMessage");
System.out.println("[Lifecycle] 2. init() method called - Initialization completed");
}
- 紧接着构造方法执行,也只执行一次
- 用于初始化资源(如数据库连接、配置参数等)
- 通过ServletConfig获取初始化参数(参数可在 web.xml 中配置)
(3)服务方法
java
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("[Lifecycle] 3. service() method called - Start processing request");
super.service(request, response);
}
- 每次客户端请求都会执行
- 负责请求分发:根据 HTTP 方法(GET/POST 等)调用对应的doXxx()方法
- 这里调用了父类的service()方法,实现了自动分发功能
(4)处理 GET 请求
java
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置响应内容类型和编码
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
// 输出HTML响应内容
out.println("<h1>Hello, World! (Jakarta EE)</h1>");
out.println("<p>Initialization Info: " + initMessage + "</p>");
}
}
- 专门处理 HTTP GET 请求的方法
- response.setContentType():设置响应类型为 HTML 并指定编码为 UTF-8
- 通过PrintWriter输出 HTML 内容到客户端浏览器
- 使用 try-with-resources 语法自动关闭输出流
(5)销毁方法
java
@Override
public void destroy() {
System.out.println("[Lifecycle] 5. destroy() method called - Releasing resources");
initMessage = null; // 清空资源示例
}
- 当 Servlet 容器关闭或应用被卸载时执行,仅执行一次
- 用于释放资源,如关闭数据库连接、清理缓存等
- 是 Servlet 生命周期的最后一个阶段
③ 其他重要说明
- serialVersionUID:用于对象序列化版本控制,确保反序列化时的兼容性
- 生命周期执行顺序:构造方法 → init() → service() → doGet()/doPost() → destroy()
- Servlet 是单实例多线程的:一个 Servlet 类只有一个实例,容器会创建多个线程处理并发请求
这个示例完整展示了 Servlet 从创建、初始化、处理请求到销毁的整个生命周期
4. 运行结果
在访问以下网址时
bash
http://localhost:8080/lifecycle_war_exploded/HelloWorld

控制台会输出以下内容
当我们关闭时

容器也会被销毁