会话跟踪与三大作用域¶
本节目标:让数据“活”得久一点
“HTTP 是健忘的,但业务需要记忆。”
当你刷新页面时,服务器怎么知道“你还是你”?(会话跟踪) 当你在不同页面跳转时,数据该存在哪里?(作用域)
本节我们将攻克 Web 开发中最核心的“数据管理”难题:
- Cookie & Session:解决“你是谁”的问题(会话跟踪)。
- 三大作用域:解决“数据存多久”的问题(生命周期管理)。
- Request:一次请求(像闪现)。
- Session:一次会话(像短期租房)。
- ServletContext:整个应用(像永久买房)。
🤯 第一步:为什么需要会话跟踪?¶
HTTP 协议是无状态 (Stateless) 的。
打个比方:HTTP 服务器就像一个“失忆的收银员”。 1. 你买了一瓶水,结账。(请求 A) 2. 你转身又拿了一包薯片,去结账。(请求 B) 3. 收银员会问:“先生您好,请问有会员卡吗?” —— 他完全不记得刚才接待过你!
为了让服务器“记住”用户(比如保持登录状态、购物车商品),我们需要给每个用户发一个“信物”。
🍪 第二步:Cookie (客户端技术)¶
Cookie 是服务器发送给浏览器的一小段文本信息。浏览器把它存下来,以后每次访问这个服务器,都会自动带上。
1. 核心机制¶
- 颁发:服务器通过响应头
Set-Cookie给浏览器。 - 携带:浏览器通过请求头
Cookie把数据带回给服务器。 - 限制:只能存字符串,大小有限(4KB),且不安全(用户可见)。
2. 实战代码:“记住上次访问时间”¶
Cookie 的坑
req.getCookies()如果没有 Cookie 会返回null,不判空会报 NullPointerException。- Cookie 只能存 ASCII 字符串,存中文必须先用
URLEncoder.encode()编码。
🔐 第三步:Session (服务端技术)¶
Session 是 Java Web 提供的服务端会话技术。它在服务器内存中为每个用户开辟了一个独立的储物柜。
1. 原理图解:钥匙与柜子¶
用户手里只拿一把钥匙(Session ID),具体的数据(如用户对象、购物车)都在服务器的柜子里。
sequenceDiagram
autonumber
participant C as 浏览器 (Client)
participant S as 服务器 (Tomcat)
Note over C, S: 场景:用户第一次访问
C->>S: 发送请求 (不带 ID)
S->>S: 1. 创建 Session 对象<br/>2. 生成唯一 ID: JSESSIONID=A101
S-->>C: 响应 (Set-Cookie: JSESSIONID=A101)
Note over C, S: 场景:用户第二次访问
C->>S: 发送请求 (Cookie: JSESSIONID=A101)
S->>S: 1. 发现 Cookie 里有 ID<br/>2. 在内存里找 ID=A101 的 Session<br/>3. 找到了!取出数据
S-->>C: 响应
2. 实战代码:Session 登录校验¶
Session 可以存任意类型的对象(Object),非常适合存用户信息。
🌍 第四步:ServletContext (全局应用域)¶
如果说 Session 是属于某个用户的私有储物柜,那么 ServletContext 就是整个大楼的“公共公告栏”。
1. 核心概念¶
- 唯一性:一个 Web 应用(Project)只有一个 ServletContext 对象。
- 共享性:所有用户、所有 Servlet 都能访问同一个 ServletContext。
- 生命周期:最长。服务器启动时创建,服务器关闭时销毁。
2. 实战代码:网站访问计数器¶
由于它是全局共享的,非常适合存放 “在线人数”、“网站总访问量” 或 “系统全局配置”。
线程安全问题
因为 ServletContext 是全局共享的,当多个用户同时修改同一个属性(如上面的 count++)时,会有线程安全问题。在生产环境中,通常配合数据库或 Redis 使用,尽量避免在 Context 中频繁修改可变数据。
⚔️ 第五步:Java Web 三大作用域对比¶
在 Java Web 开发中,我们经常需要在不同的地方“传值”。根据数据存活的时间长短,分为三大作用域对象:
| 作用域 (Scope) | 对应对象 | 存活范围 | 典型应用场景 |
|---|---|---|---|
| 请求域 (Request) | HttpServletRequest |
一次请求 (请求转发有效,重定向失效) | 页面间传值 (如 Servlet 查完数据传给 JSP/Thymeleaf 显示) |
| 会话域 (Session) | HttpSession |
一次会话 (浏览器打开到关闭,或超时) | 用户登录状态、购物车、验证码 |
| 应用域 (Application) | ServletContext |
整个应用 (服务器启动到关闭) | 全局配置、统计在线人数、共享字典数据 |
比喻记忆法 (作用域版)
- Request (便签):同事间递一张纸条,看完就扔了。(一次性)
- Session (储物柜):你专用的柜子,存你的私人物品,别人打不开。(私有长效)
- ServletContext (公告栏):大厅里的黑板,贴个通知,全公司的人都能看到。(全局共享)
🧪 第六步:随堂实验¶
练习:简单的登录 + 首页验证
需求:
- LoginServlet: 接收用户名。如果登录成功,将用户名存入 Session:
session.setAttribute("user", username); - HomeServlet: 这是一个受保护的页面。
- 先尝试从 Session 获取 "user"。
- 如果不为空:显示 "欢迎回来,xxx"。
- 如果为空(或 Session 不存在):重定向回登录页,并提示 "请先登录"。
🤔 思考:为什么关闭浏览器后再打开,Session 就丢了?
(提示:因为存
JSESSIONID的那个 Cookie 默认是会话级的,浏览器一关就没了)。
📝 总结¶
- HTTP 是无状态的,所以我们需要会话跟踪。
- Cookie:数据在客户端,不安全,适合存不重要的小数据。
- Session:数据在服务端,安全,依赖 Cookie 传输 JSESSIONID。
- ServletContext:数据在服务端,全局共享,适合存所有用户公用的数据。
- 核心 API:
req.getCookies()/resp.addCookie()req.getSession()/session.setAttribute()getServletContext()/context.setAttribute()