sesstion 如何创建

论坛 期权论坛 脚本     
已经选择匿名的用户   2021-10-25 13:35   2863   0
Session对象的创建一般是源于这样的一条语句:
Session session = request.getSession(false);或者Session session = request.getSession();如果不在乎服务器压力可能多那么一点点的话。

在Tomcat的实现中,这个request是org.apache.catalina.connector.Request类的包装类org.apache.catalina.connector.RequestFacade的对象,它的两个#getSession()方法如下:

Java代码 复制代码 收藏代码
  1. public HttpSession getSession(boolean create) {
  2. if (request == null) {
  3. throw new IllegalStateException(
  4. sm.getString("requestFacade.nullRequest"));
  5. }
  6. if (SecurityUtil.isPackageProtectionEnabled()){
  7. return (HttpSession)AccessController.
  8. doPrivileged(new GetSessionPrivilegedAction(create));
  9. } else {
  10. return request.getSession(create);
  11. }
  12. }
    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);
        }
    }


Java代码 复制代码 收藏代码
  1. public HttpSession getSession() {
  2. if (request == null) {
  3. throw new IllegalStateException(
  4. sm.getString("requestFacade.nullRequest"));
  5. }
  6. return getSession(true);
  7. }
    public HttpSession getSession() {
        if (request == null) {
            throw new IllegalStateException(
                            sm.getString("requestFacade.nullRequest"));
        }

        return getSession(true);
    }


其实差不太多,最后都会进入org.apache.catalina.connector.Request的#getSession()方法。这个方法的源代码如下:

Java代码 复制代码 收藏代码
  1. public HttpSession getSession(boolean create) {
  2. Session session = doGetSession(create);
  3. if (session != null) {
  4. return session.getSession();
  5. } else {
  6. return null;
  7. }
  8. }
    public HttpSession getSession(boolean create) {
        Session session = doGetSession(create);
        if (session != null) {
            return session.getSession();
        } else {
            return null;
        }
    }


然后调用就到了#doGetSession()这个方法了。源代码如下

Java代码 复制代码 收藏代码
  1. protected Session doGetSession(boolean create) {
  2. // 没有Context的话直接返回null
  3. if (context == null)
  4. return (null);
  5. // 判断Session是否有效
  6. if ((session != null) && !session.isValid())
  7. session = null;
  8. if (session != null)
  9. return (session);
  10. // 返回Manager对象,这里是StandardManager类的对象
  11. Manager manager = null;
  12. if (context != null)
  13. manager = context.getManager();
  14. if (manager == null)
  15. return (null); // Sessions are not supported
  16. // 判断是否有SessionID
  17. if (requestedSessionId != null) {
  18. try {
  19. // 在Manager中根据SessionID查找Session
  20. session = manager.findSession(requestedSessionId);
  21. } catch (IOException e) {
  22. session = null;
  23. }
  24. if ((session != null) && !session.isValid())
  25. session = null;
  26. if (session != null) {
  27. // 更新访问时间
  28. session.access();
  29. return (session);
  30. }
  31. }
  32. // 创建新的Session
  33. if (!create)
  34. return (null);
  35. if ((context != null) && (response != null) && context.getCookies()
  36. && response.getResponse().isCommitted()) {
  37. throw new IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted"));
  38. }
  39. // 判断是否使用 "/" 作为Session Cookie的存储路径 并且 是否SessionID来自Cookie
  40. if (connector.getEmptySessionPath() && isRequestedSessionIdFromCookie()) {
  41. // 创建Session
  42. session = manager.createSession(getRequestedSessionId());
  43. } else {
  44. session = manager.createSession(null);
  45. }
  46. // 创建一个新的Session Cookies
  47. if ((session != null) && (getContext() != null) && getContext().getCookies()) {
  48. Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME, session.getIdInternal());
  49. // 配置Session Cookie
  50. configureSessionCookie(cookie);
  51. // 在响应中加入Session Cookie
  52. response.addCookieInternal(cookie);
  53. }
  54. if (session != null) {
  55. // 更新访问时间
  56. session.access();
  57. return (session);
  58. } else {
  59. return (null);
  60. }
  61. }
 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进行了提取,源代码分别如下:

Java代码 复制代码 收藏代码
  1. protected void parseSessionId(org.apache.coyote.Request req, Request request) {
  2. ByteChunk uriBC = req.requestURI().getByteChunk();
  3. // 判断URL中是不是有";jsessionid="这个字符串
  4. int semicolon = uriBC.indexOf(match, 0, match.length(), 0);
  5. if (semicolon > 0) {
  6. // Parse session ID, and extract it from the decoded request URI
  7. // 在URL中提取Session ID
  8. int start = uriBC.getStart();
  9. int end = uriBC.getEnd();
  10. int sessionIdStart = semicolon + match.length();
  11. int semicolon2 = uriBC.indexOf(';', sessionIdStart);
  12. if (semicolon2 >= 0) {
  13. request.setRequestedSessionId(new String(uriBC.getBuffer(), start + sessionIdStart,
  14. semicolon2 - sessionIdStart));
  15. byte[] buf = uriBC.getBuffer();
  16. for (int i = 0; i < end - start - semicolon2; i++) {
  17. buf[start + semicolon + i] = buf[start + i + semicolon2];
  18. }
  19. uriBC.setBytes(buf, start, end - start - semicolon2 + semicolon);
  20. } else {
  21. request.setRequestedSessionId(new String(uriBC.getBuffer(), start + sessionIdStart,
  22. (end - start) - sessionIdStart));
  23. uriBC.setEnd(start + semicolon);
  24. }
  25. // 设定Session ID来自于URL
  26. request.setRequestedSessionURL(true);
  27. } else {
  28. request.setRequestedSessionId(null);
  29. request.setRequestedSessionURL(false);
  30. }
  31. }
 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);
  }

 }


Java代码 复制代码 收藏代码
  1. protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
  2. Context context = (Context) request.getMappingData().context;
  3. if (context != null && !context.getCookies())
  4. return;
  5. // 返回Cookie
  6. Cookies serverCookies = req.getCookies();
  7. int count = serverCookies.getCookieCount();
  8. if (count <= 0)
  9. return;
  10. for (int i = 0; i < count; i++) {
  11. ServerCookie scookie = serverCookies.getCookie(i);
  12. // 判断是否有JSESSIONID这个名字的Cookie
  13. if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
  14. // Override anything requested in the URL
  15. if (!request.isRequestedSessionIdFromCookie()) {
  16. // 设定Session ID
  17. convertMB(scookie.getValue());
  18. request.setRequestedSessionId(scookie.getValue().toString());
  19. // 如果之前在URL中读到了SessionID,那么会覆盖它
  20. request.setRequestedSessionCookie(true);
  21. request.setRequestedSessionURL(false);
  22. if (log.isDebugEnabled())
  23. log.debug(" Requested cookie session id is " + request.getRequestedSessionId());
  24. } else {
  25. if (!request.isRequestedSessionIdValid()) {
  26. convertMB(scookie.getValue());
  27. request.setRequestedSessionId(scookie.getValue().toString());
  28. }
  29. }
  30. }
  31. }
  32. }
 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()方法。这个方法是在它的基类中定义的,源代码如下:

Java代码 复制代码 收藏代码
  1. public Session findSession(String id) throws IOException {
  2. if (id == null)
  3. return (null);
  4. return (Session) sessions.get(id);
  5. }
    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的方法了:

Java代码 复制代码 收藏代码
  1. public void load() throws ClassNotFoundException, IOException {
  2. if (SecurityUtil.isPackageProtectionEnabled()) {
  3. try {
  4. AccessController.doPrivileged(new PrivilegedDoLoad());
  5. } catch (PrivilegedActionException ex) {
  6. Exception exception = ex.getException();
  7. if (exception instanceof ClassNotFoundException) {
  8. throw (ClassNotFoundException) exception;
  9. } else if (exception instanceof IOException) {
  10. throw (IOException) exception;
  11. }
  12. if (log.isDebugEnabled())
  13. log.debug("Unreported exception in load() " + exception);
  14. }
  15. } else {
  16. doLoad();
  17. }
  18. }
 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,源代码如下:

Java代码 复制代码 收藏代码
  1. protected void doLoad() throws ClassNotFoundException, IOException {
  2. if (log.isDebugEnabled())
  3. log.debug("Start: Loading persisted sessions");
  4. // 清空Map
  5. sessions.clear();
  6. // 对应work/Catalina/localhost/%app name%/SESSIONS.ser文件
  7. File file = file();
  8. if (file == null)
  9. return;
  10. if (log.isDebugEnabled())
  11. log.debug(sm.getString("standardManager.loading", pathname));
  12. FileInputStream fis = null;
  13. ObjectInputStream ois = null;
  14. Loader loader = null;
  15. ClassLoader classLoader = null;
  16. try {
  17. // 载入Session缓存文件
  18. fis = new FileInputStream(file.getAbsolutePath());
  19. BufferedInputStream bis = new BufferedInputStream(fis);
  20. if (container != null)
  21. loader = container.getLoader();
  22. if (loader != null)
  23. classLoader = loader.getClassLoader();
  24. if (classLoader != null) {
  25. if (log.isDebugEnabled())
  26. log.debug("Creating custom object input stream for class loader ");
  27. ois = new CustomObjectInputStream(bis, classLoader);
  28. } else {
  29. if (log.isDebugEnabled())
  30. log.debug("Creating standard object input stream");
  31. ois = new ObjectInputStream(bis);
  32. }
  33. } catch (FileNotFoundException e) {
  34. if (log.isDebugEnabled())
  35. log.debug("No persisted data file found");
  36. return;
  37. } catch (IOException e) {
  38. log.error(sm.getString("standardManager.loading.ioe", e), e);
  39. if (ois != null) {
  40. try {
  41. ois.close();
  42. } catch (IOException f) {
  43. ;
  44. }
  45. ois = null;
  46. }
  47. throw e;
  48. }
  49. synchronized (sessions) {
  50. try {
  51. // 读出Session个数
  52. Integer count = (Integer) ois.readObject();
  53. int n = count.intValue();
  54. if (log.isDebugEnabled())
  55. log.debug("Loading " + n + " persisted sessions");
  56. // 读入Session
  57. for (int i = 0; i < n; i++) {
  58. StandardSession session = getNewSession();
  59. session.readObjectData(ois);
  60. session.setManager(this);
  61. sessions.put(session.getIdInternal(), session);
  62. session.activate();
  63. sessionCounter++;
  64. }
  65. } catch (ClassNotFoundException e) {
  66. log.error(sm.getString("standardManager.loading.cnfe", e), e);
  67. if (ois != null) {
  68. try {
  69. ois.close();
  70. } catch (IOException f) {
  71. ;
  72. }
  73. ois = null;
  74. }
  75. throw e;
  76. } catch (IOException e) {
  77. log.error(sm.getString("standardManager.loading.ioe", e), e);
  78. if (ois != null) {
  79. try {
  80. ois.close();
  81. } catch (IOException f) {
  82. ;
  83. }
  84. ois = null;
  85. }
  86. throw e;
  87. } finally {
  88. try {
  89. if (ois != null)
  90. ois.close();
  91. } catch (IOException f) {
  92. }
  93. // 删除Session缓存文件
  94. if (file != null && file.exists())
  95. file.delete();
  96. }
  97. }
  98. if (log.isDebugEnabled())
  99. log.debug("Finish: Loading persisted sessions");
  100. }
 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()方法:

Java代码 复制代码 收藏代码
  1. public Session createSession(String sessionId) {
  2. if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) {
  3. rejectedSessions++;
  4. throw new IllegalStateException(sm.getString("standardManager.createSession.ise"));
  5. }
  6. return (super.createSession(sessionId));
  7. }
 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()方法了。

Java代码 复制代码 收藏代码
  1. public Session createSession(String sessionId) {
  2. // 创建一个新的Session
  3. Session session = createEmptySession();
  4. // 初始化Session的属性
  5. session.setNew(true);
  6. session.setValid(true);
  7. session.setCreationTime(System.currentTimeMillis());
  8. session.setMaxInactiveInterval(this.maxInactiveInterval);
  9. // 如果Session ID为null,那么就生成一个
  10. if (sessionId == null) {
  11. sessionId = generateSessionId();
  12. }
  13. session.setId(sessionId);
  14. sessionCounter++;
  15. return (session);
  16. }
 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就创建出来了。
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:86
帖子:240
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP