關(guān)于ClassLoader In Tomcat 的研究

          Posted on 2007-05-04 09:22 久城 閱讀(4143) 評(píng)論(7)  編輯  收藏 所屬分類: Java轉(zhuǎn)載

          收回五一假期那顆破碎的心,繼續(xù)我的程序人生。

          今天打算研究下ClassLoader在Tomcat中的工作。

          以下文字來(lái)自http://hi.baidu.com/wuzejian/blog/item/678c21e92aa6483eb90e2d21.html

          1 - Tomcat的類載入器的結(jié)構(gòu)

          Tomcat Server在啟動(dòng)的時(shí)候?qū)?gòu)造一個(gè)ClassLoader樹,以保證模塊的類庫(kù)是私有的
          Tomcat Server的ClassLoader結(jié)構(gòu)如下:
          其中:
          - Bootstrap - 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar
          - System - 載入$CLASSPATH/*.class
          - Common - 載入$CATALINA_HOME/common/...,它們對(duì)TOMCAT和所有的WEB APP都可見(jiàn)
          - Catalina - 載入$CATALINA_HOME/server/...,它們僅對(duì)TOMCAT可見(jiàn),對(duì)所有的WEB APP都不可見(jiàn)
          - Shared - 載入$CATALINA_HOME/shared/...,它們僅對(duì)所有WEB APP可見(jiàn),對(duì)TOMCAT不可見(jiàn)(也不必見(jiàn))
          - WebApp? - 載入ContextBase?/WEB-INF/...,它們僅對(duì)該WEB APP可見(jiàn)

           

          2 - ClassLoader的工作原理

          每個(gè)運(yùn)行中的線程都有一個(gè)成員contextClassLoader,用來(lái)在運(yùn)行時(shí)動(dòng)態(tài)地載入其它類
          系統(tǒng)默認(rèn)的contextClassLoader是systemClassLoader,所以一般而言java程序在執(zhí)行時(shí)可以使用JVM自帶的類、$JAVA_HOME/jre/lib/ext/中的類和$CLASSPATH/中的類
          可以使用Thread.currentThread().setContextClassLoader(...);更改當(dāng)前線程的contextClassLoader,來(lái)改變其載入類的行為

          ClassLoader被組織成樹形,一般的工作原理是:
          1) 線程需要用到某個(gè)類,于是contextClassLoader被請(qǐng)求來(lái)載入該類
          2) contextClassLoader請(qǐng)求它的父ClassLoader來(lái)完成該載入請(qǐng)求
          3) 如果父ClassLoader無(wú)法載入類,則contextClassLoader試圖自己來(lái)載入

          注意:WebApp?ClassLoader的工作原理和上述有少許不同:
          它先試圖自己載入類(在ContextBase?/WEB-INF/...中載入類),如果無(wú)法載入,再請(qǐng)求父ClassLoader完成

          由此可得:
          - 對(duì)于WEB APP線程,它的contextClassLoader是WebApp?ClassLoader
          - 對(duì)于Tomcat Server線程,它的contextClassLoader是CatalinaClassLoader

          3 - 部分原代碼分析

          3.1 - org/apache/catalina/startup/Bootstrap.java

          Tomcat Server線程的起點(diǎn)
          構(gòu)造ClassLoader樹,并設(shè)置Tomcat Server線程的contextClassLoader為catalinaloader
          載入若干類,然后轉(zhuǎn)入org.apache.catalina.startup.Catalina類中

          ---------------------------------------
          package org.apache.catalina.startup;

          // JDK類庫(kù)

          import java.io.File;
          import java.io.IOException;
          import java.lang.reflect.Method;
          import java.net.MalformedURLException;
          import java.net.URL;
          import java.util.ArrayList;

          // apache自己的類庫(kù)

          import org.apache.catalina.loader.Extension;
          import org.apache.catalina.loader.StandardClassLoader;

          /**
           * Boostrap loader for Catalina.     This application constructs a class loader
           * for use in loading the Catalina internal classes (by accumulating all of the
           * JAR files found in the "server" directory under "catalina.home"), and
           * starts the regular execution of the container.     The purpose of this
           * roundabout approach is to keep the Catalina internal classes (and any
           * other classes they depend on, such as an XML parser) out of the system
           * class path and therefore not visible to application level classes.
           *
           * 
          @author Craig R. McClanahan
           * 
          @version $Revision: 1.36 $ $Date: 2002/04/01 19:51:31 $
           
          */


          /**
           * 該類的main方法的主要任務(wù): --------------------------
           * 
           * 1,創(chuàng)建TOMCAT自己的類載入器(ClassLoader) +---------------------------+ | Bootstrap | | | | |
           * System | | | | | Common | | / \ | | Catalina Shared |
           * +---------------------------+ 其中: - Bootstrap -
           * 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar - System - 載入$CLASSPATH/*.class -
           * Common - 載入$CATALINA_HOME/common/,它們對(duì)TOMCAT和所有的WEB APP都可見(jiàn) - Catalina -
           * 載入$CATALINA_HOME/server/,它們僅對(duì)TOMCAT可見(jiàn),對(duì)所有的WEB APP都不可見(jiàn) - Shared -
           * 載入$CATALINA_HOME/shared/,它們僅對(duì)所有WEB APP可見(jiàn),對(duì)TOMCAT不可見(jiàn)(也不必見(jiàn))
           * 注意:當(dāng)一個(gè)ClassLoader被請(qǐng)求載入一個(gè)類時(shí),它首先請(qǐng)求其父ClassLoader完成載入,
           * 僅當(dāng)其父ClassLoader無(wú)法載入該類時(shí),才試圖自己載入該類 2,改變本身線程的默認(rèn)ClassLoader(本線程就是Tomcat
           * Server線程,類載入器是catalinaLoader)
           * 3,讓catalinaLoader載入一些類,類的位置在$CATALINA_HOME/server/lib/catalina.jar中
           * 4,創(chuàng)建org.apache.catalina.startup.Catalina類的一個(gè)實(shí)例startupInstance,并為其調(diào)用方法:
           * startupInstance.setParentClassLoader(sharedLoader);
           * startupInstance.process(args);
           * 
           * 
           * 有關(guān)ClassLoader的說(shuō)明: -----------------------
           * 
           * 每個(gè)被DEPLOY的WEB APP都會(huì)被創(chuàng)建一個(gè)ClassLoader,用來(lái)載入該WEB APP自己的類
           * 這些類的位置是webappX/WEB-INF/classes/*.class和webappX/WEB-INF/lib/*.jar
           * 
           * ClassLoader的工作流程是: 1) 收到一個(gè)載入類的的請(qǐng)求 2) 請(qǐng)求其父ClassLoader來(lái)完成該類的載入 3)
           * 如果父ClassLoader無(wú)法載入,則自己試圖完成該類的載入
           * 
           * 特別注意WEB APP自己的ClassLoader的實(shí)現(xiàn)與眾不同: 它先試圖從WEB APP自己的目錄里載入,如果失敗則請(qǐng)求父ClassLoader的代理
           * 這樣可以讓不同的WEB APP之間的類載入互不干擾
           * 
           * WEB APP的ClassLoader的層次結(jié)構(gòu)是: +----------------------------+ | Shared | | / \
           *  | | Webapp1 Webapp2  | +----------------------------+ 故對(duì)于一個(gè)WEB
           * APP,其類載入的優(yōu)先順序如下: - /WEB-INF/classes/*.class 和 /WEB-INF/lib/*.jar - Bootstrap
           * classes of JVM - System class loader classes - $CATALINA_HOME/common/ -
           * $CATALINA_HOME/shared/
           * 
           * 
           * 小結(jié): ------
           * 
           * 綜上分析 - Tomcat Server線程使用的classLoader是Catalina - 每個(gè)WEB
           * APP線程使用的classloader是Webapp?
           * 
           
          */


          public final class Bootstrap {

              
          /**
               * DEBUG級(jí)別
               
          */


              
          private static int debug = 0;

              
          /**
               * 腳本執(zhí)行該程序時(shí),提供以下的系統(tǒng)屬性: java.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath
               * "$CLASSPATH" \ java.security.manager \
               * java.security.policy=="$CATALINA_BASE"/conf/catalina.policy \
               * catalina.base="$CATALINA_BASE" \ catalina.home="$CATALINA_HOME" \
               * java.io.tmpdir="$CATALINA_TMPDIR" \
               * 
               * 
          @param args
               *            Command line arguments to be processed
               
          */


              
          public static void main(String args[]) {

                  
          // 設(shè)置debug

                  
          for (int i = 0; i < args.length; i++{
                      
          if ("-debug".equals(args[i]))
                          debug 
          = 1;
                  }


                  
          // 設(shè)置好系統(tǒng)屬性catalina.base,即保證其有值

                  
          if (System.getProperty("catalina.base"== null)
                      System.setProperty(
          "catalina.base", getCatalinaHome());

                  
          // 創(chuàng)建三個(gè)ClassLoader
                  
          // 這三個(gè)對(duì)象是通過(guò)ClassLoaderFactory的靜態(tài)方法創(chuàng)建的
                  
          // 其實(shí)際類型是StandardClassLoader,完成tomcat自定義的類載入
                  
          // 這些類對(duì)非tomcat及其上的webapp的其它java程序不可見(jiàn),故用自己的Classloader載入

                  ClassLoader commonLoader 
          = null;
                  ClassLoader catalinaLoader 
          = null;
                  ClassLoader sharedLoader 
          = null;
                  
          try {

                      File unpacked[] 
          = new File[1];
                      File packed[] 
          = new File[1];
                      File packed2[] 
          = new File[2];

                      ClassLoaderFactory.setDebug(debug);

                      
          // $CATALINA_HOME/common/classes/*.class - 未壓縮的類
                      
          // $CATALINA_HOME/common/endorsed/*.jar - 壓縮的類(endorse:支持)
                      
          // $CATALINA_HOME/common/lib/*.jar - 壓縮的類
                      
          // 這些類是被tomcat server以及所有的webapp所共享的類,由commonLoader負(fù)責(zé)載入

                      unpacked[
          0= new File(getCatalinaHome(), "common" + File.separator
                              
          + "classes");
                      packed2[
          0= new File(getCatalinaHome(), "common" + File.separator
                              
          + "endorsed");
                      packed2[
          1= new File(getCatalinaHome(), "common" + File.separator
                              
          + "lib");
                      commonLoader 
          = ClassLoaderFactory.createClassLoader(unpacked,
                              packed2, 
          null);

                      
          // $CATALINA_HOME/server/classes/*.class
                      
          // $CATALINA_HOME/server/lib/*.jar
                      
          // 這些類是僅被tomcat server使用而對(duì)webapp不可見(jiàn)的類,由catalinaLoader負(fù)責(zé)載入

                      unpacked[
          0= new File(getCatalinaHome(), "server" + File.separator
                              
          + "classes");
                      packed[
          0= new File(getCatalinaHome(), "server" + File.separator
                              
          + "lib");
                      catalinaLoader 
          = ClassLoaderFactory.createClassLoader(unpacked,
                              packed, commonLoader);

                      
          // $CATALINA_BASE/shared/classes/*.class
                      
          // $CATALINA_BASE/shared/lib/*.jar
                      
          // 這些類是僅被tomcat的webapp使用的類,由sharedLoader負(fù)責(zé)載入

                      unpacked[
          0= new File(getCatalinaBase(), "shared" + File.separator
                              
          + "classes");
                      packed[
          0= new File(getCatalinaBase(), "shared" + File.separator
                              
          + "lib");
                      sharedLoader 
          = ClassLoaderFactory.createClassLoader(unpacked,
                              packed, commonLoader);

                      
          // 注意三個(gè)自己定置的ClassLoader的層次關(guān)系:
                      
          // systemClassLoader (root)
                      
          // +--- commonLoader
                      
          // +--- catalinaLoader
                      
          // +--- sharedLoader

                  }
           catch (Throwable t) {
                      log(
          "Class loader creation threw exception", t);
                      System.exit(
          1);

                  }


                  
          // 為當(dāng)前的線程更改其contextClassLoader
                  
          // 一般的線程默認(rèn)的contextClassLoader是系統(tǒng)的ClassLoader(所有其它自定義ClassLoader的父親)
                  
          // 當(dāng)該線程需要載入類時(shí),將使用自己的contextClassLoader來(lái)尋找并載入類
                  
          // 更改contextClassLoader可以更改該線程的尋找和載入類的行為,但不影響到其它線程
                  
          // 注意!Tomcat Server線程使用的是catalinaLoader

                  Thread.currentThread().setContextClassLoader(catalinaLoader);

                  
          // Load our startup class and call its process() method

                  
          try {

                      
          // 預(yù)載入catalinalLoader的一些類

                      SecurityClassLoad.securityClassLoad(catalinaLoader);

                      
          // 獲得tomcat的啟動(dòng)類:org.apache.catalina.startup.Catalina,并創(chuàng)建該類的一個(gè)實(shí)例

                      
          if (debug >= 1)
                          log(
          "Loading startup class");
                      Class startupClass 
          = catalinaLoader
                              .loadClass(
          "org.apache.catalina.startup.Catalina");
                      Object startupInstance 
          = startupClass.newInstance();

                      
          // 設(shè)置startupInstance的父ClassLoader,相當(dāng)于執(zhí)行:
                      
          // Catalina startupInstance = new Catailina();
                      
          // startupInstance.setParentClassLoader(sharedLoader);
                      
          // 詳情參考類org.apache.catalina.startup.Catalina

                      
          if (debug >= 1)
                          log(
          "Setting startup class properties");
                      String methodName 
          = "setParentClassLoader";
                      Class paramTypes[] 
          = new Class[1];
                      paramTypes[
          0= Class.forName("java.lang.ClassLoader");
                      Object paramValues[] 
          = new Object[1];
                      paramValues[
          0= sharedLoader;
                      Method method 
          = startupInstance.getClass().getMethod(methodName,
                              paramTypes);
                      method.invoke(startupInstance, paramValues);

                      
          // 使用main方法獲得的參數(shù)args來(lái)執(zhí)行process方法,相當(dāng)于:
                      
          // startupInstance.process(args);
                      
          // 詳情參考類org.apache.catalina.startup.Catalina

                      
          if (debug >= 1)
                          log(
          "Calling startup class process() method");
                      methodName 
          = "process";
                      paramTypes 
          = new Class[1];
                      paramTypes[
          0= args.getClass();
                      paramValues 
          = new Object[1];
                      paramValues[
          0= args;
                      method 
          = startupInstance.getClass().getMethod(methodName,
                              paramTypes);
                      method.invoke(startupInstance, paramValues);

                  }
           catch (Exception e) {
                      System.out.println(
          "Exception during startup processing");
                      e.printStackTrace(System.out);
                      System.exit(
          2);
                  }


              }


              
          /**
               * 返回$CATALINA_HOME變量。如果該變量沒(méi)有定義,則將之賦值為用戶的當(dāng)前工作目錄。
               
          */


              
          private static String getCatalinaHome() {
                  
          return System.getProperty("catalina.home", System
                          .getProperty(
          "user.dir"));
              }


              
          /**
               * 返回$CATALINA_BASE變量。如果該變量沒(méi)有定義,則將之賦值為$CATALINA_HOME。
               
          */


              
          private static String getCatalinaBase() {
                  
          return System.getProperty("catalina.base", getCatalinaHome());
              }


              
          /**
               * 輸出LOG信息。
               
          */


              
          private static void log(String message) {

                  System.out.print(
          "Bootstrap: ");
                  System.out.println(message);

              }


              
          /**
               * 輸出由異常引起的LOG信息。
               
          */


              
          private static void log(String message, Throwable exception) {

                  log(message);
                  exception.printStackTrace(System.out);

              }


          }
          --------------------------------------------------------
                     +-----------------------------+
                     |            Bootstrap              |
                     |                |                  |
                     |             System                |
                     |                |                  |
                     |             Common                |
                     |            /         \               |
                     |        Catalina     Shared           |
                     |                  /       \           |
                     |             WebApp1     WebApp2      |
                     +-----------------------------+
          還有很多讓我疑惑的地方,轉(zhuǎn)過(guò)來(lái)慢慢學(xué)習(xí)。
          


          歡迎來(lái)訪!^.^!
          本BLOG僅用于個(gè)人學(xué)習(xí)交流!
          目的在于記錄個(gè)人成長(zhǎng).
          所有文字均屬于個(gè)人理解.
          如有錯(cuò)誤,望多多指教!不勝感激!

          Feedback

          # re: 關(guān)于ClassLoader In Tomcat 的研究[未登錄](méi)  回復(fù)  更多評(píng)論   

          2007-05-04 10:38 by Test
          testtest

          # re: 關(guān)于ClassLoader In Tomcat 的研究  回復(fù)  更多評(píng)論   

          2007-05-04 10:56 by BeanSoft
          哥們可以研究研究熱加載, 很有意思的哦... 不用重啟可以更新類庫(kù)

          # re: 關(guān)于ClassLoader In Tomcat 的研究  回復(fù)  更多評(píng)論   

          2007-05-04 11:04 by 久城
          @BeanSoft
          樓上乃高人也。我的導(dǎo)師也提過(guò)這個(gè)內(nèi)容。很想研究研究。在tomcat中經(jīng)常遇到這樣的問(wèn)題,必須要重新啟動(dòng)Tomcat才能加載自己已更新的類。我想,剖析一下Tomcat中的ClassLoader,應(yīng)該能解決這個(gè)問(wèn)題。正好寫在畢業(yè)論文里。只是現(xiàn)在可以參考的資料好少..還在探索中..

          # re: 關(guān)于ClassLoader In Tomcat 的研究  回復(fù)  更多評(píng)論   

          2007-05-04 13:05 by BeanSoft
          高人不敢當(dāng)了. 原來(lái)看過(guò)一些, 但是感覺(jué)熱加載還是挺復(fù)雜的. 可以參考 Tomcat 里面有 Reload 字樣的文集, JBoss 的熱加載據(jù)說(shuō)做的比較好. 原來(lái)試過(guò), 但是才疏學(xué)淺, 遇到些問(wèn)題.

          org\jboss\mx\loading 這個(gè)包下面的是類加載

          Tomcat 5.0 的位于:
          org\apache\catalina\loader

          # re: 關(guān)于ClassLoader In Tomcat 的研究  回復(fù)  更多評(píng)論   

          2007-05-04 13:23 by 久城
          very thanks!

          # re: 關(guān)于ClassLoader In Tomcat 的研究[未登錄](méi)  回復(fù)  更多評(píng)論   

          2008-04-25 21:00 by Jerry
          好文章

          # re: 關(guān)于ClassLoader In Tomcat 的研究  回復(fù)  更多評(píng)論   

          2012-02-16 22:21 by cll
          Tomcat 6之后classloader 稍微有些改動(dòng)了,去掉了server 和 shared, 都有common classloader 來(lái)載入了。http://www.jianbozhu.net/2012/02/14/tomcat-classloader-demonstration/ 這篇文章做了個(gè)簡(jiǎn)單的實(shí)驗(yàn),比叫有意思。

          Copyright © 久城

          主站蜘蛛池模板: 宣恩县| 阜城县| 呼图壁县| 教育| 城口县| 沐川县| 土默特左旗| 高邑县| 鲁山县| 宜宾县| 全州县| 台中县| 手游| 岳普湖县| 贵阳市| 郴州市| 卫辉市| 启东市| 和龙市| 曲沃县| 竹北市| 镇巴县| 庆云县| 内丘县| 石棉县| 封丘县| 宁陕县| 洛阳市| 留坝县| 崇阳县| 新营市| 铜鼓县| 县级市| 濮阳县| 揭阳市| 五家渠市| 霍城县| 栖霞市| 清丰县| 阳信县| 监利县|