Session对象的创建一般是源于这样的一条语句:
Session session = request.getSession(false);或者Session session = request.getSession();如果不在乎服务器压力可能多那么一点点的话。
在Tomcat的实现中,这个request是org.apache.catalina.connector.Request类的包装类 org.apache.catalina.connector.RequestFacade的对象,它的两个#getSession()方法如下:
- public?HttpSession?getSession(boolean?create)?{??
- ????if?(request?==?null)?{??
- ????????throw?new?IllegalStateException(??
- ????????????????????????sm.getString("requestFacade.nullRequest"));??
- ????}??
- ??
- ????if?(SecurityUtil.isPackageProtectionEnabled()){??
- ????????return?(HttpSession)AccessController.??
- ????????????doPrivileged(new?GetSessionPrivilegedAction(create));??
- ????}?else?{??
- ????????return?request.getSession(create);??
- ????}??
- }??
- public?HttpSession?getSession()?{??
- ????if?(request?==?null)?{??
- ????????throw?new?IllegalStateException(??
- ????????????????????????sm.getString("requestFacade.nullRequest"));??
- ????}??
- ??
- ????return?getSession(true);??
- }??
其实差不太多,最后都会进入org.apache.catalina.connector.Request的#getSession()方法。这个方法的源代码如下:
- public?HttpSession?getSession(boolean?create)?{??
- ????Session?session?=?doGetSession(create);??
- ????if?(session?!=?null)?{??
- ????????return?session.getSession();??
- ????}?else?{??
- ????????return?null;??
- ????}??
- }??
然后调用就到了#doGetSession()这个方法了。源代码如下
- protected?Session?doGetSession(boolean?create)?{??
- ????//?没有Context的话直接返回null??
- ????if?(context?==?null)??
- ????????return?(null);??
- ??
- ????//?判断Session是否有效??
- ????if?((session?!=?null)?&&?!session.isValid())??
- ????????session?=?null;??
- ????if?(session?!=?null)??
- ????????return?(session);??
- ??
- ????//?返回Manager对象,这里是StandardManager类的对象??
- ????Manager?manager?=?null;??
- ????if?(context?!=?null)??
- ????????manager?=?context.getManager();??
- ????if?(manager?==?null)??
- ????????return?(null);?//?Sessions?are?not?supported??
- ????//?判断是否有SessionID??
- ????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)?{??
- ????????????//?更新访问时间??
- ????????????session.access();??
- ????????????return?(session);??
- ????????}??
- ????}??
- ??
- ????//?创建新的Session??
- ????if?(!create)??
- ????????return?(null);??
- ????if?((context?!=?null)?&&?(response?!=?null)?&&?context.getCookies()??
- ????????????&&?response.getResponse().isCommitted())?{??
- ????????throw?new?IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted"));??
- ????}??
- ??
- ????//?判断是否使用?"/"?作为Session?Cookie的存储路径?并且?是否SessionID来自Cookie??
- ????if?(connector.getEmptySessionPath()?&&?isRequestedSessionIdFromCookie())?{??
- ????????//?创建Session??
- ????????session?=?manager.createSession(getRequestedSessionId());??
- ????}?else?{??
- ????????session?=?manager.createSession(null);??
- ????}??
- ??
- ????//?创建一个新的Session?Cookies??
- ????if?((session?!=?null)?&&?(getContext()?!=?null)?&&?getContext().getCookies())?{??
- ????????Cookie?cookie?=?new?Cookie(Globals.SESSION_COOKIE_NAME,?session.getIdInternal());??
- ????????//?配置Session?Cookie??
- ????????configureSessionCookie(cookie);??
- ????????//?在响应中加入Session?Cookie??
- ????????response.addCookieInternal(cookie);??
- ????}??
- ??
- ????if?(session?!=?null)?{??
- ????????//?更新访问时间??
- ????????session.access();??
- ????????return?(session);??
- ????}?else?{??
- ????????return?(null);??
- ????}??
- ??
- }??
这个方法说明了Session创建的大致过程,首先判断requestedSessionId是否存在,如果存在,那么根据这个ID去查找 Session对象。如果requestedSessionId不存在或者没有取到Session,并且传递给#getSession(boolean) 的参数为真,那么要创建一个新的Session,并且给客户端写回去一个Session Cookie。
首先,我感兴趣的是requestedSessionId的赋值,它到底是什么时候被赋值的呢?
还要向回看Tomcat的请求处理过程,请求曾到过这一步,org.apache.catalina.connector.CoyoteAdapter的#service()方法。里边有这样一句方法调用:postParseRequest(req, request, res, response)。就是这一步处理了SessionID的获取,这个方法调用了#parseSessionId()和 parseSessionCookiesId()这两个方法,就是它对Session ID进行了提取,源代码分别如下:
- protected?void?parseSessionId(org.apache.coyote.Request?req,?Request?request)?{??
- ??
- ????ByteChunk?uriBC?=?req.requestURI().getByteChunk();??
- ????//?判断URL中是不是有";jsessionid="这个字符串??
- ????int?semicolon?=?uriBC.indexOf(match,?0,?match.length(),?0);??
- ??
- ????if?(semicolon?>?0)?{??
- ????????//?Parse?session?ID,?and?extract?it?from?the?decoded?request?URI??
- ????????//?在URL中提取Session?ID??
- ????????int?start?=?uriBC.getStart();??
- ????????int?end?=?uriBC.getEnd();??
- ??
- ????????int?sessionIdStart?=?semicolon?+?match.length();??
- ????????int?semicolon2?=?uriBC.indexOf(';',?sessionIdStart);??
- ????????if?(semicolon2?>=?0)?{??
- ????????????request.setRequestedSessionId(new?String(uriBC.getBuffer(),?start?+?sessionIdStart,??
- ????????????????????semicolon2?-?sessionIdStart));??
- ????????????byte[]?buf?=?uriBC.getBuffer();??
- ????????????for?(int?i?=?0;?i?<?end?-?start?-?semicolon2;?i++)?{??
- ????????????????buf[start?+?semicolon?+?i]?=?buf[start?+?i?+?semicolon2];??
- ????????????}??
- ????????????uriBC.setBytes(buf,?start,?end?-?start?-?semicolon2?+?semicolon);??
- ????????}?else?{??
- ????????????request.setRequestedSessionId(new?String(uriBC.getBuffer(),?start?+?sessionIdStart,??
- ????????????????????(end?-?start)?-?sessionIdStart));??
- ????????????uriBC.setEnd(start?+?semicolon);??
- ????????}??
- ????????//?设定Session?ID来自于URL??
- ????????request.setRequestedSessionURL(true);??
- ??
- ????}?else?{??
- ????????request.setRequestedSessionId(null);??
- ????????request.setRequestedSessionURL(false);??
- ????}??
- ??
- }??
- protected?void?parseSessionCookiesId(org.apache.coyote.Request?req,?Request?request)?{??
- ????Context?context?=?(Context)?request.getMappingData().context;??
- ????if?(context?!=?null?&&?!context.getCookies())??
- ????????return;??
- ??
- ????//?返回Cookie??
- ????Cookies?serverCookies?=?req.getCookies();??
- ????int?count?=?serverCookies.getCookieCount();??
- ????if?(count?<=?0)??
- ????????return;??
- ??
- ????for?(int?i?=?0;?i?<?count;?i++)?{??
- ????????ServerCookie?scookie?=?serverCookies.getCookie(i);??
- ????????//?判断是否有JSESSIONID这个名字的Cookie??
- ????????if?(scookie.getName().equals(Globals.SESSION_COOKIE_NAME))?{??
- ????????????//?Override?anything?requested?in?the?URL??
- ????????????if?(!request.isRequestedSessionIdFromCookie())?{??
- ????????????????//?设定Session?ID??
- ????????????????convertMB(scookie.getValue());??
- ????????????????request.setRequestedSessionId(scookie.getValue().toString());??
- ????????????????//?如果之前在URL中读到了SessionID,那么会覆盖它??
- ????????????????request.setRequestedSessionCookie(true);??
- ????????????????request.setRequestedSessionURL(false);??
- ????????????????if?(log.isDebugEnabled())??
- ????????????????????log.debug("?Requested?cookie?session?id?is?"?+?request.getRequestedSessionId());??
- ????????????}?else?{??
- ????????????????if?(!request.isRequestedSessionIdValid())?{??
- ????????????????????convertMB(scookie.getValue());??
- ????????????????????request.setRequestedSessionId(scookie.getValue().toString());??
- ????????????????}??
- ????????????}??
- ????????}??
- ????}??
- ??
- }??
Tomcat就是通过上边的两个方法读到URL或者Cookie中存放的Session ID的。
了解了Session ID的获取,下面要看一下Session的查找过程,就是org.apache.catalina.session.StandardManager的#findSession()方法。这个方法是在它的基类中定义的,源代码如下:
- public?Session?findSession(String?id)?throws?IOException?{??
- ????if?(id?==?null)??
- ????????return?(null);??
- ????return?(Session)?sessions.get(id);??
- }??
代码很短,其中sessions是一个ConcurrentHashMap<String, Session>对象。那么这个sessions的对象是什么时候载入的Session呢?
启动的时候!可以看一下StandardManager#start()方法。最后调用了#load()方法,这个就是载入Session的方法了:
- public?void?load()?throws?ClassNotFoundException,?IOException?{??
- ????if?(SecurityUtil.isPackageProtectionEnabled())?{??
- ????????try?{??
- ????????????AccessController.doPrivileged(new?PrivilegedDoLoad());??
- ????????}?catch?(PrivilegedActionException?ex)?{??
- ????????????Exception?exception?=?ex.getException();??
- ????????????if?(exception?instanceof?ClassNotFoundException)?{??
- ????????????????throw?(ClassNotFoundException)?exception;??
- ????????????}?else?if?(exception?instanceof?IOException)?{??
- ????????????????throw?(IOException)?exception;??
- ????????????}??
- ????????????if?(log.isDebugEnabled())??
- ????????????????log.debug("Unreported?exception?in?load()?"?+?exception);??
- ????????}??
- ????}?else?{??
- ????????doLoad();??
- ????}??
- }??
最后调用了#doLoad()方法来具体的载入Session,源代码如下:
- protected?void?doLoad()?throws?ClassNotFoundException,?IOException?{??
- ????if?(log.isDebugEnabled())??
- ????????log.debug("Start:?Loading?persisted?sessions");??
- ??
- ????//?清空Map??
- ????sessions.clear();??
- ??
- ????//?对应work/Catalina/localhost/%app?name%/SESSIONS.ser文件??
- ????File?file?=?file();??
- ????if?(file?==?null)??
- ????????return;??
- ????if?(log.isDebugEnabled())??
- ????????log.debug(sm.getString("standardManager.loading",?pathname));??
- ????FileInputStream?fis?=?null;??
- ????ObjectInputStream?ois?=?null;??
- ????Loader?loader?=?null;??
- ????ClassLoader?classLoader?=?null;??
- ????try?{??
- ????????//?载入Session缓存文件??
- ????????fis?=?new?FileInputStream(file.getAbsolutePath());??
- ????????BufferedInputStream?bis?=?new?BufferedInputStream(fis);??
- ????????if?(container?!=?null)??
- ????????????loader?=?container.getLoader();??
- ????????if?(loader?!=?null)??
- ????????????classLoader?=?loader.getClassLoader();??
- ????????if?(classLoader?!=?null)?{??
- ????????????if?(log.isDebugEnabled())??
- ????????????????log.debug("Creating?custom?object?input?stream?for?class?loader?");??
- ????????????ois?=?new?CustomObjectInputStream(bis,?classLoader);??
- ????????}?else?{??
- ????????????if?(log.isDebugEnabled())??
- ????????????????log.debug("Creating?standard?object?input?stream");??
- ????????????ois?=?new?ObjectInputStream(bis);??
- ????????}??
- ????}?catch?(FileNotFoundException?e)?{??
- ????????if?(log.isDebugEnabled())??
- ????????????log.debug("No?persisted?data?file?found");??
- ????????return;??
- ????}?catch?(IOException?e)?{??
- ????????log.error(sm.getString("standardManager.loading.ioe",?e),?e);??
- ????????if?(ois?!=?null)?{??
- ????????????try?{??
- ????????????????ois.close();??
- ????????????}?catch?(IOException?f)?{??
- ????????????????;??
- ????????????}??
- ????????????ois?=?null;??
- ????????}??
- ????????throw?e;??
- ????}??
- ??
- ????synchronized?(sessions)?{??
- ????????try?{??
- ????????????//?读出Session个数??
- ????????????Integer?count?=?(Integer)?ois.readObject();??
- ????????????int?n?=?count.intValue();??
- ????????????if?(log.isDebugEnabled())??
- ????????????????log.debug("Loading?"?+?n?+?"?persisted?sessions");??
- ????????????//??读入Session??
- ????????????for?(int?i?=?0;?i?<?n;?i++)?{??
- ????????????????StandardSession?session?=?getNewSession();??
- ????????????????session.readObjectData(ois);??
- ????????????????session.setManager(this);??
- ????????????????sessions.put(session.getIdInternal(),?session);??
- ????????????????session.activate();??
- ????????????????sessionCounter++;??
- ????????????}??
- ????????}?catch?(ClassNotFoundException?e)?{??
- ????????????log.error(sm.getString("standardManager.loading.cnfe",?e),?e);??
- ????????????if?(ois?!=?null)?{??
- ????????????????try?{??
- ????????????????????ois.close();??
- ????????????????}?catch?(IOException?f)?{??
- ????????????????????;??
- ????????????????}??
- ????????????????ois?=?null;??
- ????????????}??
- ????????????throw?e;??
- ????????}?catch?(IOException?e)?{??
- ????????????log.error(sm.getString("standardManager.loading.ioe",?e),?e);??
- ????????????if?(ois?!=?null)?{??
- ????????????????try?{??
- ????????????????????ois.close();??
- ????????????????}?catch?(IOException?f)?{??
- ????????????????????;??
- ????????????????}??
- ????????????????ois?=?null;??
- ????????????}??
- ????????????throw?e;??
- ????????}?finally?{??
- ????????????try?{??
- ????????????????if?(ois?!=?null)??
- ????????????????????ois.close();??
- ????????????}?catch?(IOException?f)?{??
- ????????????}??
- ??
- ????????????//?删除Session缓存文件??
- ????????????if?(file?!=?null?&&?file.exists())??
- ????????????????file.delete();??
- ????????}??
- ????}??
- ??
- ????if?(log.isDebugEnabled())??
- ????????log.debug("Finish:?Loading?persisted?sessions");??
- }??
大致知道了Session的读取过程,后面就是Session没找到时创建Session的过程了。具体就是org.apache.catalina.session.StandardManager的#createSession()方法:
- public?Session?createSession(String?sessionId)?{??
- ????if?((maxActiveSessions?>=?0)?&&?(sessions.size()?>=?maxActiveSessions))?{??
- ????????rejectedSessions++;??
- ????????throw?new?IllegalStateException(sm.getString("standardManager.createSession.ise"));??
- ????}??
- ????return?(super.createSession(sessionId));??
- }??
最后调用到了它的基类的#createSession()方法了。
- public?Session?createSession(String?sessionId)?{??
- ????//?创建一个新的Session??
- ????Session?session?=?createEmptySession();??
- ??
- ????//?初始化Session的属性??
- ????session.setNew(true);??
- ????session.setValid(true);??
- ????session.setCreationTime(System.currentTimeMillis());??
- ????session.setMaxInactiveInterval(this.maxInactiveInterval);??
- ????//?如果Session?ID为null,那么就生成一个??
- ????if?(sessionId?==?null)?{??
- ????????sessionId?=?generateSessionId();??
- ????}??
- ????session.setId(sessionId);??
- ????sessionCounter++;??
- ??
- ????return?(session);??
- ??
- }??
通过上述过程,一个新的Session就创建出来了。