一般,每個部署在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