Tomcat中对于Session相关的框架和查询Session
Servlet中, Session代表着用户会话, 开发人员经常用Session存储一些数据
Session是什么Tomcat如何对Session进行管理的。
两个接口: Session 和 HttpSession, StandardSession 实现了这两个接口, StandardSessionFacade 实现了 HttpSession, StandardSessionFacade 包含了 StandardSession。
HttpSessionHttpSession就是在Servlet规范中标准的Session定义。注意HttpSession的全路径是javax.servlet.http.HttpSessionSessionSession接口是Catalina内部的Session的标准定义。全路径org.apache.catalina.SessionStandardSessionCatalina内部Session接口的标准实现,基础功能的提供类,需要重点分析,全路径org.apache.catalina.session.StandardSessionStandardSessionFacadeStandardSession的外观类,也是Catalina内部的类,之所以存在这个类是因为不想直接让开发人员操作到StandardSession,这样可以保证类的安全。该类的全路径org.apache.catalina.session.StandardSession如何管理Session 的, 比如创建和销毁, org.apache.catalina.Manager

Manager
Catalina中session管理器概念的顶层接口。其中定义了一些对session的基本操作,如创建,查找,添加,移除以及对特定session对象的属性的修改。除了基本操作以外还定义了一些特殊的例如session的序列化反序列化等等。
ManagerBase
抽象类,对Manager接口作了基本实现,方便继承类的复写以及实现,就像其他xxxBase类起到了一样的作用。
StandardManager
Catalina中默认的Session管理器的实现类
PersistentManagerBase
Session管理器中打标持久化Session的父类, 虽然StandardManager也可以将Session持久化,但是只是将Session持久化为一个 文件, PersistentManagerBase类和StandardManager类的区别在于前者的存储器的表现形式可以有多种,比如数据库,文件等。
PersistentManager
在PersistentManagerBase基础上增加了两个属性。
DistributedManager
从PersistentManagerBase和BackupManager类中抽象出来的一个接口,这个接口表示了两者一个共同的属性:不会持有所有session的对象,但是能找到所有的session。例如PersistentManagerBase可以将session同时存放在内存和持久化介质中。
ClusterManager
分布式集群session处理器父类,定义了一些基本方法例如获取所有tomcat集群的机器,获取其他集群的信息等基本功能。
ClusterManagerBase
抽象类,对ClusterManager作了基本实现。
BackupManager
集群间session复制策略的一种实现,会话数据只有一个备份节点,这个备份节点的位置集群中所有节点都可见。
DeltaManager
集群建session复制策略的一种实现,采用的方式是只复制差异部分,是分布式集群session同步中最好的同步方式。
在Servlet中我们使用HttpServletRequest的getSession()方法来获取session对象,而真正执行getSession方法的其实是org.apache.catalina.connector.RequestFacade对象

RequestFacade对象实现了HttpServletRequest内部封装了org.apache.catalina.connector.Request对象
@Override
public HttpSession getSession() {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
return getSession(true);
}
@Override
public HttpSession getSession(boolean create) {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
if (SecurityUtil.isPackageProtectionEnabled()){
return AccessController.
doPrivileged(new GetSessionPrivilegedAction(create));
} else {
return request.getSession(create);
}
}
最终调用的还是org.apache.catalina.connector.Request对象的getSession()方法
@Override
public HttpSession getSession(boolean create) {
Session session = doGetSession(create);
if (session == null) {
return null;
}
return session.getSession();
}
doGetSession()
protected Session doGetSession(boolean create) {
// 判断Context
Context context = getContext();
if (context == null) {
return null;
}
// 判断Session
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return session;
}
// 获取跟context绑定的sessionManager 这里默认返回的是StandardManager
Manager manager = context.getManager();
if (manager == null) {
return null; // Sessions are not supported
}
if (requestedSessionId != null) {
try {
// 根据requestSessionId 查询指定的session
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e);
} else {
log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage()));
}
session = null;
}
// 判断获取到的session是否有效
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
// session有效 增加访问时间和次数
session.access();
return session;
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return null;
}
boolean trackModesIncludesCookie =
context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE);
if (trackModesIncludesCookie && response.getResponse().isCommitted()) {
throw new IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted"));
}
// 如果没有找到已存在的session并且 要求创建新session
// 获取此次request对应的sessionid
String sessionId = getRequestedSessionId();
...
session = manager.createSession(sessionId);
// 创建cookie
if (session != null && trackModesIncludesCookie) {
Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie(
context, session.getIdInternal(), isSecure());
// cookie设置到session中
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
// 增加访问时间和次数
session.access();
return session;
}
manager.findSession()
@Override
public Session findSession(String id) throws IOException {
if (id == null) {
return null;
}
return sessions.get(id);
}
protected Map<String, Session> sessions = new ConcurrentHashMap<>();
一个类型的session管理器,他都有一个总的session容器,就是一个ConcurrentHashMap实例,key是sessionId,value是对应的session对象。
session的map中没有找到session,所以需要创建一个新的session
manager.createSession()
@Override
public Session createSession(String sessionId) {
//判断可容纳session数量
if ((maxActiveSessions >= 0) &&
(getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new TooManyActiveSessionsException(
sm.getString("managerBase.createSession.ise"),
maxActiveSessions);
}
//创建一个全新的session对象
Session session = createEmptySession();
//设置基本属性
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60);
String id = sessionId;
//设置sessionId 唯一标识负
if (id == null) {
//如果id为空 新生成一个sessionId
id = generateSessionId();
}
session.setId(id);
//session数量+1
sessionCounter++;
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return (session);
}
创建新的session对象其实就是new 了一个StandardSession对象,还有generateSessionId()方法是生成一个唯一的sessionId
session.setId(id);

也就是把新创建的session放入到管理器的session容器中(ConcurrentHashMap)对象。
在doGetSession方法中最后需要关注的就是requestedSessionId是如何生成的
/**
* The requested session ID (if any) for this request.
*/
protected String requestedSessionId = null;
和request有关, requestedSessionId对应的就是我们每次请求都有一个唯一session标识符, 为了以后同一个用户再次请求的时候可以使用同一个session
用户发送一个http请求传递给Http11Processor,经由Http11Processor解析封装在 org.apache.coyote.Request然后传递给CoyoteAdapter,coyoteAdapter是一个适配器,将coyote框 架封装的org.apache.coyote.Request适配给org.apache.catalina.connector.Request,而解析的过程就在CoyoteAdapter中。

CoyoteAdapter.parsePathParameters()
protected void parsePathParameters(org.apache.coyote.Request req,
Request request) {
//转码uri
req.decodedURI().toBytes();
ByteChunk uriBC = req.decodedURI().getByteChunk();
//查询;位置
int semicolon = uriBC.indexOf(';', 0);
//设置编码
String enc = connector.getURIEncoding();
if (enc == null) {
enc = "ISO-8859-1";
}
Charset charset = null;
try {
charset = B2CConverter.getCharset(enc);
} catch (UnsupportedEncodingException e1) {
log.warn(sm.getString("coyoteAdapter.parsePathParam",
enc));
}
//省略部分代码
//循环遍历,设置所有的kv
while (semicolon > -1) {
// 11111111111111
int start = uriBC.getStart();
int end = uriBC.getEnd();
int pathParamStart = semicolon + 1;
int pathParamEnd = ByteChunk.findBytes(uriBC.getBuffer(),
start + pathParamStart, end,
new byte[] {';', '/'});
String pv = null;
if (pathParamEnd >= 0) {
if (charset != null) {
pv = new String(uriBC.getBuffer(), start + pathParamStart,
pathParamEnd - pathParamStart, charset);
}
// Extract path param from decoded request URI
byte[] buf = uriBC.getBuffer();
for (int i = 0; i < end - start - pathParamEnd; i++) {
buf[start + semicolon + i]
= buf[start + i + pathParamEnd];
}
uriBC.setBytes(buf, start,
end - start - pathParamEnd + semicolon);
} else {
if (charset != null) {
pv = new String(uriBC.getBuffer(), start + pathParamStart,
(end - start) - pathParamStart, charset);
}
uriBC.setEnd(start + semicolon);
}
if (pv != null) {
int equals = pv.indexOf('=');
if (equals > -1) {
String name = pv.substring(0, equals);
String value = pv.substring(equals + 1);
request.addPathParameter(name, value);
}
}
semicolon = uriBC.indexOf(';', semicolon);
}
}
解析url的时候第一次设置requestSessionId
从cookie中解析
protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
//如果cookie被禁用 直接返回
Context context = (Context) request.getMappingData().context;
if (context != null && !context.getServletContext()
.getEffectiveSessionTrackingModes().contains(
SessionTrackingMode.COOKIE)) {
return;
}
//获取cookie
Cookies serverCookies = req.getCookies();
int count = serverCookies.getCookieCount();
if (count <= 0) {
return;
}
//获取cookie中session的key
String sessionCookieName = SessionConfig.getSessionCookieName(context);
for (int i = 0; i < count; i++) {
ServerCookie scookie = serverCookies.getCookie(i);
//
if (scookie.getName().equals(sessionCookieName)) {
// Override anything requested in the URL
if (!request.isRequestedSessionIdFromCookie()) {
// Accept only the first session id cookie
convertMB(scookie.getValue());
//设置requestSessionId
request.setRequestedSessionId(scookie.getValue().toString());
request.setRequestedSessionCookie(true);
request.setRequestedSessionURL(false);
} else {
if (!request.isRequestedSessionIdValid()) {
// Replace the session id until one is valid
convertMB(scookie.getValue());
request.setRequestedSessionId
(scookie.getValue().toString());
}
}
}
}
}
getSession()方法中返回的是StandardSession对象的getSession()方法
@Override
public HttpSession getSession() {
Session session = doGetSession(true);
if (session == null) {
return null;
}
return session.getSession();
}
@Override
public HttpSession getSession() {
if (facade == null){
if (SecurityUtil.isPackageProtectionEnabled()){
final StandardSession fsession = this;
facade = AccessController.doPrivileged(
new PrivilegedAction<StandardSessionFacade>(){
@Override
public StandardSessionFacade run(){
return new StandardSessionFacade(fsession);
}
});
} else {
facade = new StandardSessionFacade(this);
}
}
return (facade);
}
可以看出最后getSession()方法返回的并不是StandardSession对象,而是StandardSession对象的外观类StandardSessionFacade
之所以存在这个类是因为不想直接让开发人员操作到StandardSession,这样可以保证类的安全。