一般,每个部署在Tomcat上的Web项目(Context)都会有一个会话管理器与之关联。负责会话的建立、获取、更新、删除。
下面来个简单的示例: java
Manager manager = null; if (context != null) manager = context.getManager();//从Context中获取Manager if (manager == null) return (null); if (requestedSessionId != null) { try { //manager根据sessionid获取session session = manager.findSession(requestedSessionId); }catch (IOException e) { session = null; } if ((session != null) && !session.isValid()) session = null; if (session != null) { return (session.getSession()); } } if (!create) return (null); ... session = manager.createSession();//manager建立session if (session != null) return (session.getSession()); else return (null); }
Session Manager默认在内存中存放Session对象,它还能够持久化Session对象到文件或数据库中。本文后面会介绍。 数据库
在servlet编程中,一个session对象由javax.servlet.http.HttpSession接口表示,在一个Manager中,一个session对象由org.apache.catalina.Session接口表示。
StandardSession实现了javax.servlet.http.HttpSession和org.apache.catalina.Session接口。
咱们经过javax.servlet.http.HttpServletRequest的HttpSession getSession()方法能够获取当前会话。
出于安全考虑,Manager传递给咱们的是一个叫作StandardSessionFacade类(只实现了javax.servlet.http.HttpSession接口),而不是StandardSession,这样作能够有效防止servlet开发者调用他不应看到的定义在org.apache.catalina.Session接口中的方法。
他们的关系如图:
apache
Session接口
StandardSessionFacade是StandardSession做为HttpSession时的Facade。
Manager也和一个Facade一块儿工做:Session 编程
public interface Session { public static final String SESSION_CREATED_EVENT = "createSession"; public static final String SESSION_DESTROYED_EVENT = "destroySession"; public String getAuthType(); public void setAuthType(String authType); public long getCreationTime(); public void setCreationTime(long time); public String getId();//id做为一个session对象在Context中的惟一标示 public void setId(String id); public String getInfo(); public long getLastAccessedTime();//Manager调用这个方法判断session是否过时、非法 public Manager getManager();//一个session对象一般包含在一个Manager中 public void setManager(Manager manager); public int getMaxInactiveInterval(); public void setMaxInactiveInterval(int interval); public void setNew(boolean isNew); public Principal getPrincipal(); public void setPrincipal(Principal principal); public HttpSession getSession(); public void setValid(boolean isValid); public boolean isValid(); public void access(); public void addSessionListener(SessionListener listener); public void expire(); public Object getNote(String name); public Iterator getNoteNames(); public void recycle(); public void removeNote(String name); public void removeSessionListener(SessionListener listener); public void setNote(String name, Object value); }StandardSession
//实现java.lang.Serializable接口使session可序列化,反序列化 public class StandardSession implements Serializable{ //构造方法,强制与一个Manager关联 public StandardSession(Manager manager){ this.manager = manager; } //session属性 private HashMap attributes = new HashMap(); private transient String authType = null;//transient表示不进行序列化的属性 private long creationTime = 0L; private transient boolean expiring = false; private transient StandardSessionFacade facade = null; private String id = null; private long lastAccessedTime = creationTime; private transient ArrayList listeners = new ArrayList(); private Manager manager = null; private int maxInactiveInterval = -1; private boolean isNew = false; private boolean isValid = false; private long thisAccessedTime = creationTime; ...... public HttpSession getSession() { if (facade == null) facade = new StandardSessionFacade(this);//返回一个Facade return (facade); } }Session过时
Manager维护的一个后台线程会调用session的expire方法 安全
public void expire(boolean notify) { if (expiring) return;//已过时标示 expiring = true; setValid(false); if (manager != null) manager.remove(this);//从Manager管理的活跃会话中删除 String keys [] = keys(); for(int i = 0; i < keys.length; i++) removeAttribute(keys[i], notify);//把与session关联的对象解绑 if (notify) {//通知对SESSION_DESTROYED_EVENT感兴趣的监听器 fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null); } Context context = (Context) manager.getContainer(); Object listeners[] = context.getApplicationListeners(); if (notify && (listeners != null)) { HttpSessionEvent event = new HttpSessionEvent(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) { try { fireContainerEvent(context, "afterSessionDestroyed", listener); }catch(Exception e}{ ; } log(sm.getString("standardSession.sessionEvent"), t); } } } expiring = false; if ((manager != null) && (manager instanceof ManagerBase)){ recycle(); } }
Manger
Manager负责管理session,org.apache.catalina.session包中有一个抽象类ManagerBase,它有两个子类StandardManager、PersistentManagerBase。
StandardManager运行时:把session存放在内存中;中止时:把session存放在一个文件中,当再一次启动时,从文件中把session恢复到内存中来。
PersistentManagerBase:把session存放在二级存储中,如硬盘。它有两个子类PersistentManager、DistributedManager。
Manager接口:
session
public interface Manager { public Container getContainer(); public void setContainer(Container container); public DefaultContext getDefaultContext(); public void setDefaultContext(DefaultContext defaultContext); public boolean getDistributable(); public void setDistributable(boolean distributable); public String getInfo(); public int getMaxInactiveInterval(); public void setMaxInactiveInterval(int interval); public void add(Session session);//加入到session pool中 public void addPropertyChangeListener(PropertyChangeListener listener); public Session createSession(); public Session findSession(String id) throws IOException;public Session[] findSessions(); public void load() throws ClassNotFoundException, IOException;//从一个特定存储中加载 public void remove(Session session); public void removePropertyChangeListener(PropertyChangeListener listener); public void unload() throws IOException;//存放到特定存储中 }ManagerBase:
public abstract class ManagerBase implements Manager { ...... protected HashMap sessions = new HashMap(); public void add(Session session) { synchronized (sessions) { sessions.put(session.getId(), session); } } public void remove(Session session) { synchronized (sessions) { sessions.remove(session.getId()); } } public Session findSession(String id) throws IOException { if (id == null) return (null); synchronized (sessions) { Session session = (Session) sessions.get(id); return (session); } } ...... }PersistentManagerBase
private Store store = null;session对象能够 备份(back up)到Store,而且能够从Store中 换出 (swap out)。
DistributedManager
DistributedManager在集群环境(两台以上机器)中使用。在集群中会有一个个结点(Node),不一样的结点能够在一台机器中,也能够在不一样机器中。在集群环境中,每个结点必须拥有一个DistributedManager实例做为结点的Manager,负责Session同步(复制)。
当一个Session对象创建/销毁的时候,它所在的结点就要通知其余结点进行复制/销毁。
Tomcat提供了两个类来收发通知:ClusterSender发送通知,ClusterReceiver接收通知。 this
public Session createSession() { Session session = super.createSession(); ObjectOutputStream oos = null; ByteArrayOutputStream bos = null; ByteArraylnputStream bis = null; try { bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(new BufferedOutputStream(bos)); ((StandardSession)session).writeObjectData(oos); oos.close(); byte[] obs = bos.toByteArray(); clusterSender.send(obs); if(debug > 0) log("Replicating Session: "+session.getId()); } catch (IOException e) { log("An error occurred when replicating Session: " + session.getId()); } retun (session); }同时,DistribubedManager实现了java.lang.Runnable来确保有一个单独的线程接收其它结点发来的消息。
public void run() { while (!threadDone) { threadSleep(); processClusterReceiver(); processExpires(); processPersistenceChecks(); } }Store接口
public interface Store { public String getInfo(); public Manager getManager(); public void setManager(Manager manager); public int getSize() throws IOException; public void addPropertyChangeListener(PropertyChangeListener listener); public String[] keys() throws IOException; public Session load(String id)throws ClassNotFoundException, IOException; public void remove(String id) throws IOException; public void clear() throws IOException; pubiic void removePropertyChangeListener(PropertyChangeListener listener); public void save(Session session) throws IOException; }
StoreBase是一个抽象类,为两个子类提供了一些通用的方法,可是没有实现save和load方法,由于他们依赖于不一样类型的存储。
FileStore表明文件存储:使用java.io.ObjectOutputStream来序列化session,java.io.ObjectInputStream来反序列化session。
JDBCStore表明数据库存储。
spa