上一章讨论过Tomcat5.0中Catalina通过Manager来完成session的管理,这章我们来看看Session的标准实现:StandardSession。
StandardSession
StandardSession除了实现了javax.servlet.http.HttpSession和org.apache.catalina.Session之外,它还实现了java.lang.Serializable接口来使得Session对象可序列化,这为了后面的Session的持久化做准备。其实如果配置不持久化的话,也可以不实现序列化。
publicStandardSession(Manager manager)
由于一个Session对象常常被一个管理器持有,所以接口提供了setManager和getManager方法来关联一个Session对象和一个管理器。另外,一个Session实例在跟管理器相关联的容器有一个唯一的ID。对于该ID有setId和getId方法相关。
每次一个Session被调用的时候,都会调用access方法更新它的最后访问时间和次数
publicvoid access() {
this.lastAccessedTime =this.thisAccessedTime;
this.thisAccessedTime = System.currentTimeMillis();
evaluateIfValid();
accessCount++;
}
结束Session使用
publicvoid endAccess() {
isNew = false;
accessCount--;
}
在Manager创建Session的时候,会把新生成的SessionId放入Session实例,这时候session设置Id并关联manager,然后触发相应事件监听。
publicvoid setId(String id) {
if ((this.id !=null) && (manager !=null))
manager.remove(this); //如果Id重复,直接覆盖
this.id = id;
if (manager !=null)
manager.add(this);
tellNew();
}
getSession 外观模式
Connector拼装StandardSession后,最后会传给servlet处理。出于安全和数据隐藏的原因,不会将一个StandardSession实例传直接递给servlet。而是返回外观类StandardSessionFacade。
StandardSession类的逻辑很复杂,有很多的属性和方法,例如引用Manager来管理Session;打印log;触发监听器等等。
但是传给Servlet,只需要显示一小部分信息,没必要全部暴露,所以创建一个对外的外观类来隐藏信息。
public HttpSession getSession() {
if (facade ==null){
if (System.getSecurityManager() !=null){
final StandardSession fsession =this;
facade = (StandardSessionFacade)AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
return newStandardSessionFacade(fsession);
}
});
}else {
facade = new StandardSessionFacade(this);
}
}
return (facade);
}
超时-回收
校验Session是否有效,accessCount>0正在被使用,视为有效;如果超时则视为无效。
publicboolean isValid() {
if (this.expiring)return true;
if (!this.isValid )return false;
if (accessCount > 0)return true;
if (maxInactiveInterval >= 0) {
long timeNow = System.currentTimeMillis();
int timeIdle = (int) ((timeNow -thisAccessedTime) / 1000L);
if (timeIdle >= maxInactiveInterval)
expire(true);
}
return (this.isValid);
}
一个Session对象如果在由maxInactiveInterval变量的时间内没有access则视为无效,使用expire()来终结。
A 置可用性isValid为false,置使用关联accessCount为0
B 解除manager和Session的关联;
C 清除Session的属性;
D 触发事件监听器;
publicvoid expire(boolean notify) {
if (expiring)return; //置失效过了,直接返回
synchronized (this) {
if (manager ==null) return;
expiring = true;
Context context = (Context)manager.getContainer();
Object listeners[] =context.getApplicationLifecycleListeners();
if (notify && (listeners != null)) {
HttpSessionEvent event =newHttpSessionEvent(getSession());
for (int i = 0; i < listeners.length; i++) {
int j = (listeners.length - 1) - i;
if (!(listeners[j] instanceof HttpSessionListener))
continue;
HttpSessionListener listener = (HttpSessionListener) listeners[j];
try {
fireContainerEvent(context,"beforeSessionDestroyed",listener);
listener.sessionDestroyed(event);
fireContainerEvent(context,"afterSessionDestroyed",listener);
}catch (Throwable t) { }
}
}
accessCount = 0;
setValid(false);
if (manager !=null)
manager.remove(this);
expiring = false; //失效状态一前一后,防止一个Session重复expire
String keys[] = (String[])attributes.keySet().toArray(EMPTY_ARRAY)
for (int i = 0; i < keys.length; i++)
removeAttributeInternal(keys[i], notify);
}
}