前言
在现代Java Web开发中,Servlet作为最基础的组件之一,扮演着至关重要的角色。无论你是刚入门Java Web开发的新手,还是有一定经验的开发者,深入理解Servlet的工作原理都能帮助你构建更高效、更可靠的Web应用。本文将全面剖析Servlet的生命周期、请求响应机制以及Cookie/Session管理,带你掌握Servlet的核心知识。
一、Servlet 生命周期详解
Servlet的生命周期是理解其工作原理的基础,它由Web容器(如Tomcat)管理,主要包括三个阶段:初始化、服务请求和销毁。
1.1 生命周期阶段
public class LifeCycleServlet extends HttpServlet {
// 初始化阶段
@Override
public void init() throws ServletException {
System.out.println("Servlet初始化...");
// 通常在这里进行资源加载等一次性操作
}
// 服务阶段(处理请求)
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("处理请求...");
super.service(req, resp);
}
// 销毁阶段
@Override
public void destroy() {
System.out.println("Servlet销毁...");
// 释放资源
}
}
1.1.1 初始化阶段
触发时机:当容器第一次收到对该Servlet的请求时,或容器启动时(取决于配置)
执行方法:
init()
方法,仅执行一次常见用途:
加载配置文件
建立数据库连接池
初始化全局变量
配置Servlet启动顺序:
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.example.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup> <!-- 数字越小优先级越高 -->
</servlet>
1.1.2 服务阶段
触发时机:每次客户端请求该Servlet时
执行方法:
service()
方法,对于HTTP请求,通常分派到doGet()
、doPost()
等方法特点:
多线程环境下运行(需注意线程安全问题)
每次请求都会创建新的请求和响应对象
1.1.3 销毁阶段
触发时机:容器关闭或应用卸载时
执行方法:
destroy()
方法,仅执行一次常见用途:
释放数据库连接
保存状态信息
关闭文件流等资源
1.2 线程安全问题
由于Servlet是单例多线程的,需要注意线程安全问题:
public class UnsafeServlet extends HttpServlet {
private int count = 0; // 实例变量,存在线程安全问题
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
count++;
// 这里会出现竞态条件
}
}
解决方案:
使用局部变量而非实例变量
对共享资源加锁(synchronized)
使用原子类(AtomicInteger等)
二、请求与响应机制
Servlet的核心功能就是处理请求并生成响应,理解HttpServletRequest和HttpServletResponse是关键。
2.1 HttpServletRequest详解
HttpServletRequest封装了所有的HTTP请求信息:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// 获取请求参数
String username = request.getParameter("username");
// 获取请求头
String userAgent = request.getHeader("User-Agent");
// 获取客户端信息
String clientIP = request.getRemoteAddr();
// 获取请求URI和URL
String uri = request.getRequestURI();
StringBuffer url = request.getRequestURL();
// 获取Cookie
Cookie[] cookies = request.getCookies();
// 获取Session
HttpSession session = request.getSession();
}
重要方法:
getParameter()
:获取表单参数getParameterValues()
:获取多值参数(如复选框)getAttribute()
/setAttribute()
:请求域属性getRequestDispatcher().forward()
:请求转发
2.2 HttpServletResponse详解
HttpServletResponse用于构建HTTP响应:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// 设置响应类型和编码
response.setContentType("text/html;charset=UTF-8");
// 设置状态码
response.setStatus(HttpServletResponse.SC_OK); // 200
// 添加响应头
response.addHeader("Cache-Control", "no-cache");
// 重定向
response.sendRedirect("/newLocation");
// 写入响应体
PrintWriter out = response.getWriter();
out.println("<html><body>Hello World</body></html>");
// 设置Cookie
Cookie cookie = new Cookie("user", "admin");
response.addCookie(cookie);
}
2.3 请求转发与重定向
请求转发(Forward):
服务器端行为
客户端不知情
URL不变
共享request对象
request.getRequestDispatcher("/target").forward(request, response);
重定向(Redirect):
客户端行为
服务器返回302状态码和新URL
浏览器发起新请求
URL改变
不共享request对象
response.sendRedirect("/newLocation");
三、Cookie与Session管理
HTTP协议是无状态的,为了保持用户状态,我们需要使用Cookie和Session。
3.1 Cookie详解
Cookie是服务器发送到浏览器并保存在本地的小数据片段。
创建Cookie:
Cookie cookie = new Cookie("username", "john_doe");
// 设置有效期(秒),0表示删除,负数表示会话Cookie
cookie.setMaxAge(60 * 60 * 24); // 1天
// 设置路径,只有匹配路径才会发送Cookie
cookie.setPath("/");
// 安全设置
cookie.setSecure(true); // 仅HTTPS
cookie.setHttpOnly(true); // 防止XSS
response.addCookie(cookie);
读取Cookie:
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("username".equals(cookie.getName())) {
String value = cookie.getValue();
// 处理Cookie值
}
}
}
删除Cookie:
Cookie cookie = new Cookie("username", "");
cookie.setMaxAge(0); // 立即过期
response.addCookie(cookie);
3.2 Session详解
Session是服务器端的状态保持机制,基于Cookie或URL重写实现。
Session基本使用:
// 获取Session,如果不存在则创建
HttpSession session = request.getSession();
// 设置Session属性
session.setAttribute("user", userObject);
// 获取Session属性
User user = (User) session.getAttribute("user");
// 使Session失效
session.invalidate();
Session配置:
<!-- web.xml中配置Session超时时间(分钟) -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
Session工作原理:
首次创建Session时,服务器会生成唯一JSESSIONID
通过Cookie将JSESSIONID返回给浏览器
后续请求浏览器会携带JSESSIONID
服务器根据JSESSIONID找到对应的Session
如果浏览器禁用了Cookie,可以通过URL重写维持Session:
String url = response.encodeURL("/secured/page.jsp");
3.3 Cookie与Session对比
特性 | Cookie | Session |
---|---|---|
存储位置 | 客户端 | 服务器端 |
安全性 | 较低(可被查看修改) | 较高 |
存储大小 | 有限(约4KB) | 较大(取决于服务器) |
生命周期 | 可长期保存 | 通常较短(会话级别) |
性能影响 | 每次请求都会携带 | 服务器需要查找存储 |
数据类型 | 仅字符串 | 任意Java对象 |
四、最佳实践与常见问题
4.1 Servlet开发最佳实践
线程安全:
避免使用实例变量
如需共享资源,使用同步或线程安全类
资源管理:
在init()中初始化资源
在destroy()中释放资源
使用try-with-resources确保资源关闭
编码规范:
始终设置内容类型和字符编码
合理使用MVC模式,Servlet作为控制器
性能优化:
合理使用Servlet缓存
避免在Servlet中进行复杂业务逻辑
4.2 常见问题解决方案
Q: 如何解决表单重复提交问题?
A: 使用Token机制:
生成唯一Token存入Session
表单中包含该Token
处理请求时验证Token并移除
Q: 如何实现文件上传?
A: 使用Servlet 3.0+的Part API:
Part filePart = request.getPart("file");
filePart.write("/path/to/save");
Q: 如何防止Session固定攻击?
A: 用户登录后重置Session:
HttpSession session = request.getSession();
session.invalidate(); // 使旧Session失效
session = request.getSession(true); // 创建新Session
结语
Servlet作为Java Web开发的基石,其重要性不言而喻。通过本文的学习,你应该已经掌握了Servlet的生命周期、请求响应处理机制以及状态管理技术。这些知识不仅是使用更高级框架(如Spring MVC)的基础,也是解决实际Web开发问题的利器。
在实际开发中,虽然我们可能很少直接编写Servlet(因为框架已经封装了大部分功能),但理解这些底层原理能帮助我们在遇到问题时更快定位和解决,也能让我们更好地理解Web应用的工作机制。
希望本文对你有所帮助,如果有任何问题或建议,欢迎在评论区留言讨论!