Session 笔记
# 100.Session 笔记
概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象 HttpSession 中。
# Session 入门
之前我们说过 request 是域对象,可以共享数据,ServletContext 也是域对象,可以共享数据;同理,HttpSession 也是一个域对象,也有如下方法,用来共享数据:
setAttribute(String name,Object value)
getAttribute(String name)
removeAttribute(String name)
使用 Session 共享数据,步骤也很简单:
- 获取 HttpSession 对象:
HttpSession session = request.getSession();
- 使用 HttpSession 对象:调用
setAttribute
,getAttribute
等方法
我们写两个 Servlet 来验证下:
package com.peterjxl.session;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/sessionDemo1")
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.setAttribute("msg", "Hello Session!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.peterjxl.session;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/sessionDemo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
Object msg = session.getAttribute("msg");
System.out.println(msg);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
重启 Tomcat,访问 http://localhost: 8080/hello/sessionDemo1,
然后访问 http://localhost: 8080/hello/sessionDemo2,可以看到后台打印了 Hello Session!
。
关闭浏览器,会话就结束了,再次访问 sessionDemo2,发现打印了 null。也就是说,在一次会话中才能使用 Session,关闭浏览器就断掉了会话
# Session 原理
访问多个 Servlet,服务器是如何确保在一次会话范围内,多次获取的 Session 对象是同一个?Session 的实现是依赖于 Cookie 的
- 当我们第一次访问 Servlet 时,也就会第一次获取 Session,服务器会创建一个 Session 对象,并且这个对象有一个唯一的 ID,假设 ID = 123456
- 返回响应消息给数据的时候,会设置 Cookie,Cookie 的名字为 JSESSIONID,值为 Session 的 ID。
- 浏览器会保存这个 Cookie,并且下次访问 Servlet 的时候,会带上 Cookie
- 服务器再次收到请求的时候,当我们获取 Session 对象的时候,服务器会根据 Session 的 ID 查找内存中有没对应的 Session 对象,有则返回
HttpSession session = req.getSession();
我们可以来验证下。关闭浏览器,然后打开控制台,访问 SessionDemo1,可以看到服务器有设置 Cookie:
注:如果不想关闭浏览器,可以通过控制台删除 Cookie,也可以实现清空的效果。
然后我们访问 SessionDemo2 的时候,会带上这个 Session 的 ID。
# Session 的细节
这里有几个小问题要确认:
- 当客户端关闭后,服务器不关闭,两次获取 session 是否为同一个?
- 客户端不关闭,服务器关闭后,两次获取的 session 是同一个吗?
- session 的失效时间?
# 客户端关闭
当客户端关闭后,服务器不关闭,两次获取 session 是否为同一个?
默认情况下,不是。客户端关闭后,认为会话关闭了。
实践:访问 Servlet 后,打印对象;然后关闭浏览器,再次访问,打印的对象和前一次不同。
HttpSession session = req.getSession();
System.out.println(session);
2
如果需要相同,则可以创建 Cookie, 键为 JSESSIONID,值就是 Session 的 ID,容纳后设置最大存活时间,让 cookie 持久化保存:
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60 * 60);
resp.addCookie(c);
2
3
全部代码:
package com.peterjxl.session;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/sessionDemo3")
public class SessionDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
System.out.println(session);
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60 * 60);
resp.addCookie(c);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 服务器关闭
客户端不关闭,服务器关闭后,两次获取的 session 是同一个吗?不是同一个,因为创建对象有一定的随机性(随机分配内存)。我们可以验证下,新写一个 Servlet:
package com.peterjxl.session;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/sessionDemo4")
public class SessionDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
System.out.println(session);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
重启 Tomcat,访问 http://localhost: 8080/hello/sessionDemo4,可以看到打印:
org.apache.catalina.session.StandardSessionFacade@6ca699f9
再次重启 Tomcat,访问,输出:很明显和之前的输出不一样
org.apache.catalina.session.StandardSessionFacade@5f59f4fe
如果不一样,有什么问题呢?比如我在某东,未登录,加了几个商品到购物车,这些数据是存储到 Session 里的;准备结算的时候接到了一个电话,打了 4 分钟,在这 4 分钟内,某东重启了一次,导致我购物车里的商品丢失了,非常影响体验,我可能就不买或者少买了东西,损失了流量。
因此我们得考虑保存下数据。这就涉及到如下概念:
- session 的钝化:在服务器正常关闭之前,将 session 对象系列化到硬盘上
- session 的活化:在服务器启动后,将 session 文件转化为内存中的 session 对象即可。
Tomcat 能自动完成以上工作
# Session 钝化、活化实验
实践:在 IDEA 中不能实现 Session 的钝化和活化,得用本地 Tomcat 才能完成。
演示步骤:
一、先停止 IDEA 里启动的 Tomcat
二、将我们的所有项目文件打成一个压缩包,然后改名为 hello.war
注意点:由于笔者之前在 img 目录下放了一个中文名的图片,导致启动的时候报错 java.lang.IllegalArgumentException: MALFORMED
at java.util.zip.ZipCoder.toString(ZipCoder.java: 58)
at java.util.zip.ZipFile.getZipEntry(ZipFile.java: 566)
at java.util.zip.ZipFile.access$900(ZipFile.java: 60)windows 环境下,默认字符集为 GBK,ZipFile 默认使用 UTF-8 字符集,当文件名存在中文时,处理时就会报错。笔者暂未找到解决方案,因此先删除了下中文名的图片
启动 Tomcat,访问路径 http://localhost: 8080/hello/sessionDemo1;
然后再访问 http://localhost: 8080/hello/sessionDemo2,可以看到 Tomcat 的黑窗口里打印了 Hello Session
查看 Tomcat 的 work 目录,一步步定义到我们的项目目录里,可以看到是空的:
正常关闭 Tomcat:点击 Tomcat 的 bin 目录下的 shutdown.bat。
发现 work 目录有了 SESSIONS.ser 文件:
重新启动服务器,该 SESSIONS.ser 文件会被自动读取,然后被删除;
再次访问 http://localhost: 8080/hello/sessionDemo2,同样打印了 msg 的值。
为什么在 IDEA 里不能完成 Session 的钝化和活化?因为 IDEA 关闭服务后,虽然会钝化(可以在 work 目录里看到 SESSIONS.ser 文件),但是在重启服务器时,会将 work 目录删除后再新建,所以活化生效了,因为 IDEA 里不能生效。
# Session 有效期
Session 什么时候会被销毁?
- 服务器关闭
- session 对象调用
invalidate()
方法。
那如果不主动销毁,Session 会一直保持在内存里吗?不是的,session 默认失效时间 30 分钟。我们可以看 Tomcat 的配置文件 apache-tomcat-9.0.73\conf\web.xml,637 行左右:
<!-- ==================== Default Session Configuration ================= -->
<!-- You can set the default session timeout (in minutes) for all newly -->
<!-- created sessions by modifying the value below. -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
2
3
4
5
6
7
通过注释我们可以知道,我们可以修改这个配置,数值是以分钟为单位。
这个配置文件是所有项目的父配置文件,不建议修改,这样可能会影响所有项目的配置;建议在自己的项目的 web.xml 文件里修改。
# session 的特点
- session 用于存储一次会话的多次请求的数据,存在服务器端
- session 可以存储任意类型,任意大小的数据(只要内存够)
session 与 Cookie 的区别:
- session 存储数据在服务器端,Cookie 在客户端
- session 没有数据大小限制,Cookie 有(Session 有主菜的意思,而 Cookie 是小饼干,谁大谁小一听便知)
- session 数据安全一点,Cookie 相对于不安全
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02