在Java Web开发中,Servlet和Filter是两个核心组件,理解它们的生命周期是掌握JavaWeb开发的关键。本文将用通俗易懂的方式解析它们的生命周期全过程。
一、Servlet生命周期:三阶段模型
Servlet的生命周期由Servlet容器(如Tomcat)管理,整个过程分为三个阶段:
1. 初始化阶段(init)
- 触发时机:当容器启动时(配置了
<load-on-startup>
)或第一次请求到达时 - 核心方法:
init(ServletConfig config)
- 只执行一次
- 典型应用场景:
public class MyServlet extends HttpServlet { private DatabaseConnection dbConnection; @Override public void init() throws ServletException { // 初始化数据库连接 dbConnection = new DatabaseConnection(); // 加载配置文件 Properties config = loadConfig(); } }
2. 服务阶段(service)
- 触发时机:每次客户端请求
- 核心方法:
service()
-> 根据请求类型调用doGet()
/doPost()
- 可执行多次(每次请求都会调用)
- 线程安全问题警示:
public class UnsafeServlet extends HttpServlet { private int count; // 存在线程安全问题! protected void doGet(HttpServletRequest request, HttpServletResponse response) { count++; // ...其他处理 } }
3. 销毁阶段(destroy)
- 触发时机:容器关闭或应用卸载时
- 核心方法:
destroy()
- 只执行一次
- 资源清理示例:
@Override public void destroy() { // 关闭数据库连接 if(dbConnection != null) { dbConnection.close(); } // 释放其他资源 releaseResources(); }
Servlet生命周期流程图
二、Filter生命周期:四阶段模型
Filter的生命周期与Servlet类似但更精细:
1. 初始化阶段(init)
- 触发时机:容器启动时
- 核心方法:
init(FilterConfig filterConfig)
- 只执行一次
- 配置参数获取示例:
public class LogFilter implements Filter { private String logPath; @Override public void init(FilterConfig config) { // 获取web.xml中配置的参数 logPath = config.getInitParameter("logDirectory"); // 初始化日志系统 Logger.init(logPath); } }
2. 过滤请求阶段(doFilter前半段)
- 触发时机:请求到达时
- 核心方法:
doFilter()
的前半部分 - 每次匹配的请求都会执行
- 请求预处理示例:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 1. 请求到达时的预处理 long startTime = System.currentTimeMillis(); logRequest(request); // 2. 关键操作:将请求传递给后续过滤器或Servlet chain.doFilter(request, response); // 3. 响应返回时的后处理(下一阶段) }
3. 过滤响应阶段(doFilter后半段)
- 触发时机:服务器生成响应后
- 核心位置:
chain.doFilter()
方法调用之后 - 每次匹配的请求都会执行
- 响应后处理示例:
@Override public void doFilter(...) { // ...请求预处理 chain.doFilter(request, response); // 分水岭 // 响应后处理 long duration = System.currentTimeMillis() - startTime; logResponse(response, duration); addSecurityHeaders(response); }
4. 销毁阶段(destroy)
- 触发时机:容器关闭时
- 核心方法:
destroy()
- 只执行一次
- 资源释放示例:
@Override public void destroy() { // 关闭日志系统 Logger.shutdown(); // 释放其他资源 cleanupResources(); }
Filter生命周期流程图
三、Servlet vs Filter生命周期对比
特性 | Servlet | Filter |
---|---|---|
初始化 | init() 一次 | init() 一次 |
处理阶段 | service()/doXxx() 多次 | doFilter() 多次 |
销毁 | destroy() 一次 | destroy() 一次 |
执行顺序 | 单实例处理请求 | 多个Filter按配置顺序执行 |
核心功能 | 处理请求并生成响应 | 预处理请求和后处理响应 |
配置方式 | @WebServlet或web.xml | @WebFilter或web.xml |
四、常见问题解答
Q1:为什么Servlet和Filter都是单例?
- 答:为了提高性能,容器为每个Servlet/Filter只创建一个实例,通过线程池处理并发请求
Q2:Filter的chain.doFilter()为什么如此重要?
- 答:这是Filter链中的关键控制点:
- 调用前:对请求进行预处理
- 调用时:将控制权交给下一个Filter或Servlet
- 调用后:对响应进行后处理
Q3:如何在生命周期中安全使用实例变量?
- 答:
- 避免在Servlet/Filter中使用可修改的实例变量
- 必须使用时需同步处理(但会影响性能)
- 推荐使用局部变量或线程安全对象
Q4:如何控制初始化顺序?
- 答:
- Servlet:通过
web.xml
中<load-on-startup>
的值控制(值越小优先级越高) - Filter:通过
web.xml
中的配置顺序或@WebFilter
的filterName排序
- Servlet:通过
五、总结
通过本文的讲解,我们应该掌握:
- Servlet生命周期:
初始化 -> 服务 -> 销毁
- Filter生命周期:
初始化 -> 请求过滤 -> 响应过滤 -> 销毁
- Filter的
doFilter
方法是关键分界点 - 两者都是单例模式,需要注意线程安全问题
- 合理使用生命周期方法进行资源管理
生命周期是JavaWeb组件的核心概念,理解它们将帮助你编写更高效、更健壮的Web应用程序。在实际开发中,建议在init()中初始化资源,在destroy()中释放资源,避免内存泄漏问题。
示例代码可在GitHub获取:https://github.com/example/servlet-filter-demo
参考文档: