深入理解 Servlet:访问流程、核心接口与生命周期
在 Java Web 开发中,Servlet 是基石般的存在。无论是传统的 JSP 还是现代的 Spring MVC,底层都离不开 Servlet 的支持。本文将围绕 Servlet 的访问流程、体系结构、生命周期以及两个重要的配置对象(ServletConfig 和 ServletContext)展开,结合流程图与类图,帮助大家彻底掌握 Servlet 的核心原理。
一、Servlet 访问流程
浏览器向 Web 服务器发起请求,服务器根据 URL 映射找到对应的 Servlet,然后执行一系列操作。整个过程可以用下面的流程图表示:
自定义Servlet Servlet容器 Web服务器 浏览器 自定义Servlet Servlet容器 Web服务器 浏览器 发起HTTP请求 解析请求,定位Servlet 创建HttpServletRequest/HttpServletResponse对象 调用service()方法 处理请求并返回响应 封装响应对象 返回HTTP响应
关键点:
- 在调用
service()方法之前,容器会自动创建HttpServletRequest和HttpServletResponse对象。 service()方法会根据请求类型(GET、POST 等)分发给对应的doGet()、doPost()方法。- 响应结束后,容器负责将响应数据发送回浏览器。
二、Servlet 体系结构(UML 类图)
Servlet 规范中,我们通常继承 HttpServlet 来编写自己的业务逻辑。下图展示了 Servlet 核心接口与类的继承关系:
实现
继承
继承
<<interface>>
Servlet
+init(ServletConfig config)
+service(ServletRequest req, ServletResponse res)
+destroy()
+getServletConfig()
+getServletInfo()
<<abstract>>
GenericServlet
-config: ServletConfig
+init(ServletConfig config)
+init()
+getServletConfig()
+getServletContext()
+log(String msg)
+abstract service(ServletRequest req, ServletResponse res)
<<abstract>>
HttpServlet
+service(ServletRequest req, ServletResponse res)
+service(HttpServletRequest req, HttpServletResponse resp)
+doGet(HttpServletRequest req, HttpServletResponse resp)
+doPost(HttpServletRequest req, HttpServletResponse resp)
+doPut(...)
+doDelete(...)
MyServlet
+doGet(HttpServletRequest req, HttpServletResponse resp)
+doPost(HttpServletRequest req, HttpServletResponse resp)
说明:
Servlet接口定义了所有 Servlet 必须实现的生命周期方法。GenericServlet抽象类提供了Servlet接口的基本实现,并增加了日志、初始化参数等辅助方法,但依然保留了service()方法为抽象。HttpServlet进一步针对 HTTP 协议,将service()方法实现为请求类型分发器,并提供了doGet()、doPost()等钩子方法。- 我们自定义的 Servlet 只需继承
HttpServlet,重写需要的doXxx()方法即可。
三、Servlet 生命周期
Servlet 由 Web 容器(如 Tomcat)管理,其生命周期分为四个阶段:加载实例化 → 初始化 → 服务 → 销毁。下面用流程图展示:
容器启动或首次请求
加载Servlet类并实例化
调用init方法初始化
等待请求
收到请求,调用service方法
容器关闭或应用卸载
调用destroy方法,释放资源
各阶段详解
| 阶段 | 时机 | 次数 | 说明 |
|---|---|---|---|
| 加载与实例化 | 首次访问 Servlet 时,或容器启动时(若配置了 <load-on-startup>) |
1次 | 通过反射调用无参构造器创建实例 |
| 初始化 | 实例化之后立即执行 | 1次 | 调用 init(ServletConfig config),可用于加载资源、获取配置 |
| 服务 | 每次收到请求 | 多次 | 调用 service() 方法,根据请求类型分发到 doGet/doPost |
| 销毁 | 容器正常关闭或应用被卸载时 | 1次 | 调用 destroy() 方法,释放数据库连接等资源 |
注意:
- 默认情况下,Servlet 在第一次被访问时 才加载实例化。若希望在容器启动时就完成加载,可以在
web.xml中配置<load-on-startup>正数(数值越小优先级越高)。 init()只执行一次,且是线程安全的(容器保证单例)。
四、ServletConfig 与 ServletContext
这两个接口是 Servlet 编程中获取配置和共享数据的关键。
1. ServletConfig
作用范围 :每个 Servlet 私有 ,由容器在 init() 方法中传入。
主要功能:
- 获取当前 Servlet 在
web.xml中配置的初始化参数(<init-param>)。 - 获取当前 Servlet 的名称(
<servlet-name>)。 - 获取全局的
ServletContext对象。
配置示例 (web.xml):
xml
<servlet>
<servlet-name>DemoServlet</servlet-name>
<servlet-class>com.example.DemoServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
代码使用:
java
String encoding = getServletConfig().getInitParameter("encoding");
String servletName = getServletConfig().getServletName();
ServletContext context = getServletConfig().getServletContext();
2. ServletContext
作用范围 :整个 Web 应用共享 ,一个应用只有一个 ServletContext 对象。
主要功能:
- 获取全局初始化参数 :在
<context-param>中配置的参数,所有 Servlet 均可读取。 - 获取 Web 资源的绝对路径 :通过
getRealPath("/")或getResourceAsStream()读取配置文件、图片等。 - 作为域对象 :在多个 Servlet 之间共享数据(
setAttribute/getAttribute)。
配置示例 (web.xml):
xml
<context-param>
<param-name>globalConfig</param-name>
<param-value>production</param-value>
</context-param>
代码使用:
java
// 获取全局参数
String config = getServletContext().getInitParameter("globalConfig");
// 获取文件真实路径
String realPath = getServletContext().getRealPath("/WEB-INF/config.properties");
// 存储共享数据
getServletContext().setAttribute("userCount", 100);
Integer count = (Integer) getServletContext().getAttribute("userCount");
五、总结
| 概念 | 核心要点 |
|---|---|
| 访问流程 | 浏览器 → Web服务器 → 容器创建 req/res → 调用 service → 返回响应 |
| 体系结构 | Servlet(接口) → GenericServlet(抽象类) → HttpServlet(抽象类) → 自定义Servlet |
| 生命周期 | 实例化(1次) → init(1次) → service(多次) → destroy(1次) |
| ServletConfig | 每个 Servlet 私有,用于获取自身初始化参数及 ServletContext |
| ServletContext | 整个应用共享,用于获取全局参数、资源路径、存储域数据 |