搜索 K
Appearance
博客正在加载中...
Appearance
概念:服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象 HttpSession 中。
之前我们说过 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!");
}
}
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);
}
} 重启 Tomcat,访问 http://localhost: 8080/hello/sessionDemo1,
然后访问 http://localhost: 8080/hello/sessionDemo2,可以看到后台打印了 Hello Session!。 关闭浏览器,会话就结束了,再次访问 sessionDemo2,发现打印了 null。也就是说,在一次会话中才能使用 Session,关闭浏览器就断掉了会话
访问多个 Servlet,服务器是如何确保在一次会话范围内,多次获取的 Session 对象是同一个?Session 的实现是依赖于 Cookie 的
HttpSession session = req.getSession();我们可以来验证下。关闭浏览器,然后打开控制台,访问 SessionDemo1,可以看到服务器有设置 Cookie:
注:如果不想关闭浏览器,可以通过控制台删除 Cookie,也可以实现清空的效果。
然后我们访问 SessionDemo2 的时候,会带上这个 Session 的 ID。
这里有几个小问题要确认:
当客户端关闭后,服务器不关闭,两次获取 session 是否为同一个?
默认情况下,不是。客户端关闭后,认为会话关闭了。 实践:访问 Servlet 后,打印对象;然后关闭浏览器,再次访问,打印的对象和前一次不同。
HttpSession session = req.getSession();
System.out.println(session); 如果需要相同,则可以创建 Cookie, 键为 JSESSIONID,值就是 Session 的 ID,容纳后设置最大存活时间,让 cookie 持久化保存:
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60 * 60);
resp.addCookie(c);全部代码:
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);
}
}客户端不关闭,服务器关闭后,两次获取的 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);
}
} 重启 Tomcat,访问 http://localhost: 8080/hello/sessionDemo4,可以看到打印:
org.apache.catalina.session.StandardSessionFacade@6ca699f9 再次重启 Tomcat,访问,输出:很明显和之前的输出不一样
org.apache.catalina.session.StandardSessionFacade@5f59f4fe如果不一样,有什么问题呢?比如我在某东,未登录,加了几个商品到购物车,这些数据是存储到 Session 里的;准备结算的时候接到了一个电话,打了 4 分钟,在这 4 分钟内,某东重启了一次,导致我购物车里的商品丢失了,非常影响体验,我可能就不买或者少买了东西,损失了流量。
因此我们得考虑保存下数据。这就涉及到如下概念:
Tomcat 能自动完成以上工作
实践:在 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 什么时候会被销毁?
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>通过注释我们可以知道,我们可以修改这个配置,数值是以分钟为单位。 这个配置文件是所有项目的父配置文件,不建议修改,这样可能会影响所有项目的配置;建议在自己的项目的 web.xml 文件里修改。