HTTP协议是无状态的,但Cookie赋予了浏览器在客户端保存会话状态的能力。此外,基于Cookie也可以间接实现在服务端保存会话状态的能力,我们通常将其称为Session。Servlet规范中,Session
对象专门用于保存服务端会话状态。这篇笔记我们学习Servlet规范中Session的用法。
Servlet规范中,Session是基于Servlet容器内存和Cookie中的JSESSIONID属性实现的。浏览器第一次访问服务端程序时,浏览器请求不会带上任何Cookie,此时服务端的响应会包含Set-Cookie
响应头来设置JSESSIONID
。浏览器后续访问时就会带上这个Cookie,在服务端,一些信息会被缓存到Servlet容器的JVM内存中,基于JSESSIONID
我们就可以判断该数据属于哪个会话。
至于集群模式如何实现Session,通常有两种方式:Session复制和统一存储。前者通常需要Servlet中间件支持Session复制,例如将Wildfly配置为Cluster模式;后者则是将Session信息统一存储到Redis等缓存数据库中,这样就避免了基于内存的Session在集群模式下出现不一致的情况。
Servlet规范对于Session进行了封装,使用非常简单,我们通常不需要考虑设置Cookie等操作,它们都由Servlet容器维护,下面例子中我们实现了Session信息的读写。
package com.gacfox.demo.demoweb;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 读取Session
String sessionData = (String) request.getSession().getAttribute("text");
System.out.println(sessionData);
// 写入Session
request.getSession().setAttribute("text", "hello");
}
}
例子代码中,我们在Session中写入和读取了一个字符串对象。
调用invalidate()
方法可以主动清空Session。
request.getSession().invalidate();
虽然我们能主动清空当前会话的Session,但Servlet规范并没有提供一个主动清空任意会话Session的能力,这本质是因为Servlet规范没有提供一个基于SessionID查找任意会话Session的接口,因此诸如“管理员踢人下线”这种功能实现起来就比较曲折和绕弯,我们不得不缓存一些额外信息来辅助Session的失效判断,这大概也是Servlet规范的一个小缺陷。
HttpSessionListener
能够在Session创建和销毁时监听到对应的消息,下面是一个例子。
package com.gacfox.demo.demoweb.listerner;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class DemoSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
String sessionId = httpSessionEvent.getSession().getId();
System.out.println(sessionId);
}
}
代码中我们监听了Session创建的消息,并打印了SessionID。