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相对于不安全