一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語言 - JAVA教程 - 深入淺析TomCat Session管理分析

深入淺析TomCat Session管理分析

2020-01-13 17:30泰山不老生 JAVA教程

這篇文章主要介紹了深入淺析TomCat Session管理分析,需要的朋友可以參考下

前言

  對于廣大java開發(fā)者而已,對于J2EE規(guī)范中的Session應(yīng)該并不陌生,我們可以使用Session管理用戶的會話信息,最常見的就是拿Session用來存放用戶登錄、身份、權(quán)限及狀態(tài)等信息。對于使用Tomcat作為Web容器的大部分開發(fā)人員而言,Tomcat是如何實(shí)現(xiàn)Session標(biāo)記用戶和管理Session信息的呢?

概要

SESSION

  Tomcat內(nèi)部定義了Session和HttpSession這兩個(gè)會話相關(guān)的接口,其類繼承體系如圖1所示。

深入淺析TomCat Session管理分析

圖1  Session類繼承體系

圖1中額外列出了Session的類繼承體系,這里對他們逐個(gè)進(jìn)行介紹。

Session:Tomcat中有關(guān)會話的基本接口規(guī)范,圖1列出了它定義的主要方法,表1對這些方法進(jìn)行介紹。

表1  Session接口說明

 

方法 描述
getCreationTime()/setCreationTime(time : long)  獲取與設(shè)置Session的創(chuàng)建時(shí)間
getId()/setId(id : String)   獲取與設(shè)置Session的ID
getThisAccessedTime() 獲取最近一次請求的開始時(shí)間
getLastAccessedTime() 獲取最近一次請求的完成時(shí)間
getManager()/setManager(manager : Manager)  獲取與設(shè)置Session管理器
getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 獲取與設(shè)置Session的最大訪問間隔
getSession() 獲取HttpSession
isValid()/setValid(isValid : boolean)  獲取與設(shè)置Session的有效狀態(tài)
access()/endAccess()  開始與結(jié)束Session的訪問
expire() 設(shè)置Session過期

 

HttpSession:在HTTP客戶端與HTTP服務(wù)端提供的一種會話的接口規(guī)范,圖1列出了它定義的主要方法,表2對這些方法進(jìn)行介紹。

表2  HttpSession接口說明

 

方法 描述
getCreationTime() 獲取Session的創(chuàng)建時(shí)間
getId() 獲取Session的ID
getLastAccessedTime() 獲取最近一次請求的完成時(shí)間
getServletContext()  獲取當(dāng)前Session所屬的ServletContext
getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 獲取與設(shè)置Session的最大訪問間隔
getAttribute(name : String) /setAttribute(name : String, value : Object) 獲取與設(shè)置Session作用域的屬性
removeAttribute(name : String) 清除Session作用域的屬性
invalidate() 使Session失效并解除任何與此Session綁定的對象

 

ClusterSession:集群部署下的會話接口規(guī)范,圖1列出了它的主要方法,表3對這些方法進(jìn)行介紹。

表3  ClusterSession接口說明

 

方法 描述
isPrimarySession() 是否是集群的主Session
setPrimarySession(boolean primarySession) 設(shè)置集群主Session

 

StandardSession:標(biāo)準(zhǔn)的HTTP Session實(shí)現(xiàn),本文將以此實(shí)現(xiàn)為例展開。

在部署Tomcat集群時(shí),需要使集群中各個(gè)節(jié)點(diǎn)的會話狀態(tài)保持同步,目前Tomcat提供了兩種同步策略:

ReplicatedSession:每次都把整個(gè)會話對象同步給集群中的其他節(jié)點(diǎn),其他節(jié)點(diǎn)然后更新整個(gè)會話對象。這種實(shí)現(xiàn)比較簡單方便,但會造成大量無效信息的傳輸。

DeltaSession:對會話中增量修改的屬性進(jìn)行同步。這種方式由于是增量的,所以會大大降低網(wǎng)絡(luò)I/O的開銷,但是實(shí)現(xiàn)上會比較復(fù)雜因?yàn)樯婕暗綄拰傩圆僮鬟^程的管理。

SESSION管理器

  Tomcat內(nèi)部定義了Manager接口用于制定Session管理器的接口規(guī)范,目前已經(jīng)有很多Session管理器的實(shí)現(xiàn),如圖2所示。

深入淺析TomCat Session管理分析

圖2  Session管理器的類繼承體系

對應(yīng)圖2中的內(nèi)容我們下面逐個(gè)描述:

Manager:Tomcat對于Session管理器定義的接口規(guī)范,圖2已經(jīng)列出了Manager接口中定義的主要方法,表4詳細(xì)描述了這些方法的作用。

表4  Manager接口說明

 

方法 描述
getContainer()/setContainer(container : Container)  獲取或設(shè)置Session管理器關(guān)聯(lián)的容器,一般為Context容器
getDistributable()/setDistributable(distributable : boolean)   獲取或設(shè)置Session管理器是否支持分布式
getMaxInactiveInterval()/setMaxInactiveInterval(interval : int)   獲取或設(shè)置Session管理器創(chuàng)建的Session的最大非活動時(shí)間間隔
getSessionIdLength()/setSessionIdLength(idLength : int)  獲取或設(shè)置Session管理器創(chuàng)建的Session ID的長度
getSessionCounter()/setSessionCounter(sessionCounter : long)   獲取或設(shè)置Session管理器創(chuàng)建的Session總數(shù)
getMaxActive()/setMaxActive(maxActive : int)  獲取或設(shè)置當(dāng)前已激活Session的最大數(shù)量
getActiveSessions()   獲取當(dāng)前激活的所有Session
getExpiredSessions()/setExpiredSessions(expiredSessions : long)  獲取或設(shè)置當(dāng)前已過期Session的數(shù)量
getRejectedSessions()/setRejectedSessions(rejectedSessions : int)  獲取或設(shè)置已拒絕創(chuàng)建Session的數(shù)量
getSessionMaxAliveTime()/setSessionMaxAliveTime(sessionMaxAliveTime : int)   獲取或設(shè)置已過期Session中的最大活動時(shí)長
getSessionAverageAliveTime()/setSessionAverageAliveTime(sessionAverageAliveTime : int)  獲取或設(shè)置已過期Session的平均活動時(shí)長
add(session : Session)/remove(session : Session)  給Session管理器增加或刪除活動Session
changeSessionId(session : Session)  給Session設(shè)置新生成的隨機(jī)Session ID
createSession(sessionId : String)  基于Session管理器的默認(rèn)屬性配置創(chuàng)建新的Session
findSession(id : String)  返回sessionId參數(shù)唯一標(biāo)記的Session
findSessions()  返回Session管理器管理的所有活動Session
load()/unload()  從持久化機(jī)制中加載Session或向持久化機(jī)制寫入Session
backgroundProcess()  容器接口中定義的為具體容器在后臺處理相關(guān)工作的實(shí)現(xiàn),Session管理器基于此機(jī)制實(shí)現(xiàn)了過期Session的銷毀

 

ManagerBase:封裝了Manager接口通用實(shí)現(xiàn)的抽象類,未提供對load()/unload()等方法的實(shí)現(xiàn),需要具體子類去實(shí)現(xiàn)。所有的Session管理器都繼承自ManagerBase。

ClusterManager:在Manager接口的基礎(chǔ)上增加了集群部署下的一些接口,所有實(shí)現(xiàn)集群下Session管理的管理器都需要實(shí)現(xiàn)此接口。

PersistentManagerBase:提供了對于Session持久化的基本實(shí)現(xiàn)。

PersistentManager:繼承自PersistentManagerBase,可以在Server.xml的<Context>元素下通過配置<Store>元素來使用。PersistentManager可以將內(nèi)存中的Session信息備份到文件或數(shù)據(jù)庫中。當(dāng)備份一個(gè)Session對象時(shí),該Session對象會被復(fù)制到存儲器(文件或者數(shù)據(jù)庫)中,而原對象仍然留在內(nèi)存中。因此即便服務(wù)器宕機(jī),仍然可以從存儲器中獲取活動的Session對象。如果活動的Session對象超過了上限值或者Session對象閑置了的時(shí)間過長,那么Session會被換出到存儲器中以節(jié)省內(nèi)存空間。

StandardManager:不用配置<Store>元素,當(dāng)Tomcat正常關(guān)閉,重啟或Web應(yīng)用重新加載時(shí),它會將內(nèi)存中的Session序列化到Tomcat目錄下的/work/Catalina/host_name/webapp_name/SESSIONS.ser文件中。當(dāng)Tomcat重啟或應(yīng)用加載完成后,Tomcat會將文件中的Session重新還原到內(nèi)存中。如果突然終止該服務(wù)器,則所有Session都將丟失,因?yàn)镾tandardManager沒有機(jī)會實(shí)現(xiàn)存盤處理。

ClusterManagerBase:提供了對于Session的集群管理實(shí)現(xiàn)。

DeltaManager:繼承自ClusterManagerBase。此Session管理器是Tomcat在集群部署下的默認(rèn)管理器,當(dāng)集群中的某一節(jié)點(diǎn)生成或修改Session后,DeltaManager將會把這些修改增量復(fù)制到其他節(jié)點(diǎn)。

BackupManager:沒有繼承ClusterManagerBase,而是直接實(shí)現(xiàn)了ClusterManager接口。是Tomcat在集群部署下的可選的Session管理器,集群中的所有Session都被全量復(fù)制到一個(gè)備份節(jié)點(diǎn)。集群中的所有節(jié)點(diǎn)都可以訪問此備份節(jié)點(diǎn),達(dá)到Session在集群下的備份效果。

  為簡單起見,本文以StandardManager為例講解Session的管理。StandardManager是StandardContext的子組件,用來管理當(dāng)前Context的所有Session的創(chuàng)建和維護(hù)。如果你應(yīng)經(jīng)閱讀或者熟悉了《Tomcat源碼分析——生命周期管理》一文的內(nèi)容,那么你就知道當(dāng)StandardContext正式啟動,也就是StandardContext的startInternal方法(見代碼清單1)被調(diào)用時(shí),StandardContext還會啟動StandardManager。

代碼清單1

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Override
  protected synchronized void startInternal() throws LifecycleException {
    // 省略與Session管理無關(guān)的代碼
        // Acquire clustered manager
        Manager contextManager = null;
        if (manager == null) {
          if ( (getCluster() != null) && distributable) {
            try {
              contextManager = getCluster().createManager(getName());
            } catch (Exception ex) {
              log.error("standardContext.clusterFail", ex);
              ok = false;
            }
          } else {
            contextManager = new StandardManager();
          }
        }
        // Configure default manager if none was specified
        if (contextManager != null) {
          setManager(contextManager);
        }
        if (manager!=null && (getCluster() != null) && distributable) {
          //let the cluster know that there is a context that is distributable
          //and that it has its own manager
          getCluster().registerManager(manager);
        }
     // 省略與Session管理無關(guān)的代碼
      try {
        // Start manager
        if ((manager != null) && (manager instanceof Lifecycle)) {
          ((Lifecycle) getManager()).start();
        }
        // Start ContainerBackgroundProcessor thread
        super.threadStart();
      } catch(Exception e) {
        log.error("Error manager.start()", e);
        ok = false;
      }
     // 省略與Session管理無關(guān)的代碼
  }

從代碼清單1可以看到StandardContext的startInternal方法中涉及Session管理的執(zhí)行步驟如下:

創(chuàng)建StandardManager;

如果Tomcat結(jié)合Apache做了分布式部署,會將當(dāng)前StandardManager注冊到集群中;
啟動StandardManager;
StandardManager的start方法用于啟動StandardManager,實(shí)現(xiàn)見代碼清單2。

代碼清單2

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
  public synchronized final void start() throws LifecycleException {
    //省略狀態(tài)校驗(yàn)的代碼if (state.equals(LifecycleState.NEW)) {
      init();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
        !state.equals(LifecycleState.STOPPED)) {
      invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }
    setState(LifecycleState.STARTING_PREP);
    try {
      startInternal();
    } catch (LifecycleException e) {
      setState(LifecycleState.FAILED);
      throw e;
    }
    if (state.equals(LifecycleState.FAILED) ||
        state.equals(LifecycleState.MUST_STOP)) {
      stop();
    } else {
      // Shouldn't be necessary but acts as a check that sub-classes are
      // doing what they are supposed to.
      if (!state.equals(LifecycleState.STARTING)) {
        invalidTransition(Lifecycle.AFTER_START_EVENT);
      }
      setState(LifecycleState.STARTED);
    }
  }

從代碼清單2可以看出啟動StandardManager的步驟如下:

調(diào)用init方法初始化StandardManager;

調(diào)用startInternal方法啟動StandardManager;

STANDARDMANAGER的初始化

   經(jīng)過上面的分析,我們知道啟動StandardManager的第一步就是調(diào)用父類LifecycleBase的init方法,關(guān)于此方法已在《Tomcat源碼分析——生命周期管理》一文詳細(xì)介紹,所以我們只需要關(guān)心StandardManager的initInternal。StandardManager本身并沒有實(shí)現(xiàn)initInternal方法,但是StandardManager的父類ManagerBase實(shí)現(xiàn)了此方法,其實(shí)現(xiàn)見代碼清單3。

代碼清單3

?
1
2
3
4
5
6
7
@Override
 protected void initInternal() throws LifecycleException {
   super.initInternal();
   setDistributable(((Context) getContainer()).getDistributable());
   // Initialize random number generation
   getRandomBytes(new byte[16]);
 }

閱讀代碼清單3,我們總結(jié)下ManagerBase的initInternal方法的執(zhí)行步驟:

將容器自身即StandardManager注冊到JMX(LifecycleMBeanBase的initInternal方法的實(shí)現(xiàn)請參考《Tomcat源碼分析——生命周期管理》一文);

從父容器StandardContext中獲取當(dāng)前Tomcat是否是集群部署,并設(shè)置為ManagerBase的布爾屬性distributable;
調(diào)用getRandomBytes方法從隨機(jī)數(shù)文件/dev/urandom中獲取隨機(jī)數(shù)字節(jié)數(shù)組,如果不存在此文件則通過反射生成java.security.SecureRandom的實(shí)例,用它生成隨機(jī)數(shù)字節(jié)數(shù)組。

注意:此處調(diào)用getRandomBytes方法生成的隨機(jī)數(shù)字節(jié)數(shù)組并不會被使用,之所以在這里調(diào)用實(shí)際是為了完成對隨機(jī)數(shù)生成器的初始化,以便將來分配Session ID時(shí)使用。

我們詳細(xì)閱讀下getRandomBytes方法的代碼實(shí)現(xiàn),見代碼清單4。

代碼清單4

  

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protected void getRandomBytes(byte bytes[]) {
   // Generate a byte array containing a session identifier
   if (devRandomSource != null && randomIS == null) {
     setRandomFile(devRandomSource);
   }
   if (randomIS != null) {
     try {
       int len = randomIS.read(bytes);
       if (len == bytes.length) {
         return;
       }
       if(log.isDebugEnabled())
         log.debug("Got " + len + " " + bytes.length );
     } catch (Exception ex) {
       // Ignore
     }
     devRandomSource = null;
     try {
       randomIS.close();
     } catch (Exception e) {
       log.warn("Failed to close randomIS.");
     }
     randomIS = null;
   }
   getRandom().nextBytes(bytes);
 }

代碼清單4中的setRandomFile

方法(見代碼清單5)用于從隨機(jī)數(shù)文件/dev/urandom中獲取隨機(jī)數(shù)字節(jié)數(shù)組。

代碼清單5

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void setRandomFile( String s ) {
    // as a hack, you can use a static file - and generate the same
    // session ids ( good for strange debugging )
    if (Globals.IS_SECURITY_ENABLED){
      randomIS = AccessController.doPrivileged(new PrivilegedSetRandomFile(s));
    } else {
      try{
        devRandomSource=s;
        File f=new File( devRandomSource );
        if( ! f.exists() ) return;
        randomIS= new DataInputStream( new FileInputStream(f));
        randomIS.readLong();
        if( log.isDebugEnabled() )
          log.debug( "Opening " + devRandomSource );
      } catch( IOException ex ) {
        log.warn("Error reading " + devRandomSource, ex);
        if (randomIS != null) {
          try {
            randomIS.close();
          } catch (Exception e) {
            log.warn("Failed to close randomIS.");
          }
        }
        devRandomSource = null;
        randomIS=null;
      }
    }
  }

代碼清單4中的setRandomFile方法(見代碼清單6)通過反射生成java.security.SecureRandom的實(shí)例,并用此實(shí)例生成隨機(jī)數(shù)字節(jié)數(shù)組。

代碼清單6

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public Random getRandom() {
    if (this.random == null) {
      // Calculate the new random number generator seed
      long seed = System.currentTimeMillis();
      long t1 = seed;
      char entropy[] = getEntropy().toCharArray();
      for (int i = 0; i < entropy.length; i++) {
        long update = ((byte) entropy[i]) << ((i % 8) * 8);
        seed ^= update;
      }
      try {
        // Construct and seed a new random number generator
        Class<?> clazz = Class.forName(randomClass);
        this.random = (Random) clazz.newInstance();
        this.random.setSeed(seed);
      } catch (Exception e) {
        // Fall back to the simple case
        log.error(sm.getString("managerBase.random", randomClass),
            e);
        this.random = new java.util.Random();
        this.random.setSeed(seed);
      }
      if(log.isDebugEnabled()) {
        long t2=System.currentTimeMillis();
        if( (t2-t1) > 100 )
          log.debug(sm.getString("managerBase.seeding", randomClass) + " " + (t2-t1));
      }
    }
    return (this.random);
  }

根據(jù)以上的分析,StandardManager的初始化主要就是執(zhí)行了ManagerBase的initInternal方法。

STANDARDMANAGER的啟動

  調(diào)用StandardManager的startInternal方法用于啟動StandardManager,見代碼清單7。

 代碼清單7

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
 protected synchronized void startInternal() throws LifecycleException {
   // Force initialization of the random number generator
   if (log.isDebugEnabled())
     log.debug("Force random number initialization starting");
   generateSessionId();
   if (log.isDebugEnabled())
     log.debug("Force random number initialization completed");
   // Load unloaded sessions, if any
   try {
     load();
   } catch (Throwable t) {
     log.error(sm.getString("standardManager.managerLoad"), t);
   }
   setState(LifecycleState.STARTING);
 }

 從代碼清單7可以看出啟動StandardManager的步驟如下:

步驟一 調(diào)用generateSessionId方法(見代碼清單8)生成新的Session ID;

代碼清單8

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
protected synchronized String generateSessionId() {
    byte random[] = new byte[16];
    String jvmRoute = getJvmRoute();
    String result = null;
    // Render the result as a String of hexadecimal digits
    StringBuilder buffer = new StringBuilder();
    do {
      int resultLenBytes = 0;
      if (result != null) {
        buffer = new StringBuilder();
        duplicates++;
      }
      while (resultLenBytes < this.sessionIdLength) {
        getRandomBytes(random);
        random = getDigest().digest(random);
        for (int j = 0;
        j < random.length && resultLenBytes < this.sessionIdLength;
        j++) {
          byte b1 = (byte) ((random[j] & 0xf0) >> 4);
          byte b2 = (byte) (random[j] & 0x0f);
          if (b1 < 10)
            buffer.append((char) ('0' + b1));
          else
            buffer.append((char) ('A' + (b1 - 10)));
          if (b2 < 10)
            buffer.append((char) ('0' + b2));
          else
            buffer.append((char) ('A' + (b2 - 10)));
          resultLenBytes++;
        }
      }
      if (jvmRoute != null) {
        buffer.append('.').append(jvmRoute);
      }
      result = buffer.toString();
    } while (sessions.containsKey(result));
    return (result);
  }

步驟二  加載持久化的Session信息。為什么Session需要持久化?由于在StandardManager中,所有的Session都維護(hù)在一個(gè)ConcurrentHashMap中,因此服務(wù)器重啟或者宕機(jī)會造成這些Session信息丟失或失效,為了解決這個(gè)問題,Tomcat將這些Session通過持久化的方式來保證不會丟失。下面我們來看看StandardManager的load方法的實(shí)現(xiàn),見代碼清單9所示。

代碼清單9

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
   }
 }

如果需要安全機(jī)制是打開的并且包保護(hù)模式打開,會通過創(chuàng)建PrivilegedDoLoad來加載持久化的Session,其實(shí)現(xiàn)如代碼清單10所示。

代碼清單10

?
1
2
3
4
5
6
7
8
9
10
private class PrivilegedDoLoad
   implements PrivilegedExceptionAction<Void> {
   PrivilegedDoLoad() {
     // NOOP
   }
   public Void run() throws Exception{
     doLoad();
     return null;
   }
 }

從代碼清單10看到實(shí)際負(fù)責(zé)加載的方法是doLoad,根據(jù)代碼清單9知道默認(rèn)情況下,加載Session信息的方法也是doLoad。所以我們只需要看看doLoad的實(shí)現(xiàn)了,見代碼清單11。

代碼清單11

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
protected void doLoad() throws ClassNotFoundException, IOException {
   if (log.isDebugEnabled())
     log.debug("Start: Loading persisted sessions");
   // Initialize our internal data structures
   sessions.clear();
   // Open an input stream to the specified pathname, if any
   File file = file();
   if (file == null)
     return;
   if (log.isDebugEnabled())
     log.debug(sm.getString("standardManager.loading", pathname));
   FileInputStream fis = null;
   BufferedInputStream bis = null;
   ObjectInputStream ois = null;
   Loader loader = null;
   ClassLoader classLoader = null;
   try {
     fis = new FileInputStream(file.getAbsolutePath());
     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 (fis != null) {
       try {
         fis.close();
       } catch (IOException f) {
         // Ignore
       }
     }
     if (bis != null) {
       try {
         bis.close();
       } catch (IOException f) {
         // Ignore
       }
     }
     throw e;
   }
   // Load the previously unloaded active sessions
   synchronized (sessions) {
     try {
       Integer count = (Integer) ois.readObject();
       int n = count.intValue();
       if (log.isDebugEnabled())
         log.debug("Loading " + n + " persisted sessions");
       for (int i = 0; i < n; i++) {
         StandardSession session = getNewSession();
         session.readObjectData(ois);
         session.setManager(this);
         sessions.put(session.getIdInternal(), session);
         session.activate();
         if (!session.isValidInternal()) {
           // If session is already invalid,
           // expire session to prevent memory leak.
           session.setValid(true);
           session.expire();
         }
         sessionCounter++;
       }
     } catch (ClassNotFoundException e) {
       log.error(sm.getString("standardManager.loading.cnfe", e), e);
       try {
         ois.close();
       } catch (IOException f) {
         // Ignore
       }
       throw e;
     } catch (IOException e) {
       log.error(sm.getString("standardManager.loading.ioe", e), e);
       try {
         ois.close();
       } catch (IOException f) {
         // Ignore
       }
       throw e;
     } finally {
       // Close the input stream
       try {
         ois.close();
       } catch (IOException f) {
         // ignored
       }
       // Delete the persistent storage file
       if (file.exists() )
         file.delete();
     }
   }
   if (log.isDebugEnabled())
     log.debug("Finish: Loading persisted sessions");
 }

 從代碼清單11看到StandardManager的doLoad方法的執(zhí)行步驟如下:

清空sessions緩存維護(hù)的Session信息;

調(diào)用file方法返回當(dāng)前Context下的Session持久化文件,比如:D:workspaceTomcat7.0workCatalinalocalhosthost-managerSESSIONS.ser;

打開Session持久化文件的輸入流,并封裝為CustomObjectInputStream;

從Session持久化文件讀入持久化的Session的數(shù)量,然后逐個(gè)讀取Session信息并放入sessions緩存中。

至此,有關(guān)StandardManager的啟動就介紹到這里,我將會在下篇內(nèi)容講解Session的分配、追蹤、銷毀等內(nèi)容。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费的网址| 黑人干我| 婷婷精品进入 | 日本高清在线不卡 | 国产精品国色综合久久 | 亚洲国产精品成 | 国产精品亚洲专区一区 | 免费观看韩剧网站在线观看 | 98pao强力打造高清免费 | 俄罗斯女人与公拘i交酡 | 九九九好热在线 | 国产9191精品免费观看 | 青青青手机在线观看 | 911精品国产亚洲日本美国韩国 | 校园春色自拍偷拍 | 福利片福利一区二区三区 | 明星ai人脸替换造梦在线播放 | 放荡护士玩3p口述 | 滑进了柔佳火热紧夹的 | 色综合伊人色综合网亚洲欧洲 | sihu国产午夜精品一区二区三区 | 亚洲天堂一区二区在线观看 | 欧美破处女视频 | 日本无翼乌漫画 | 视频二区 素人 欧美 日韩 | 99久久香蕉国产线看观香 | 精品国产免费 | 日本国产最新一区二区三区 | 欧美影院一区二区 | 日日日操| 2021国产精品成人免费视频 | 四虎永久在线精品免费影视 | 黄蓉h系列 | spy2wc48美女撒尿| 亚洲视频精选 | 波多野结衣同性系列698 | 青青久久精品国产免费看 | 国内精品一区二区在线观看 | 美女的让男人桶爽网站 | 奇米网在线 | 欧美黑大吊 |