海闊天空

          I'm on my way!
          隨筆 - 17, 文章 - 69, 評論 - 21, 引用 - 0
          數據加載中……

          啟動期間的加載類的順序和過程

          Tomcat啟動時classloader加載順序
          Tomcat的class加載的優先順序一覽  
          1.最先是$JAVA_HOME/jre/lib/ext/下的jar文件。  
          2.環境變量CLASSPATH中的jar和class文件。  
          3.$CATALINA_HOME/common/classes下的class文件。  
          4.$CATALINA_HOME/commons/endorsed下的jar文件。  
          5.$CATALINA_HOME/commons/i18n下的jar文件。  
          6.$CATALINA_HOME/common/lib   下的jar文件。  
          (JDBC驅動之類的jar文件可以放在這里,這樣就可以避免在server.xml配置好數據源卻出現找不到JDBC   Driver的情況。)  
          7.$CATALINA_HOME/server/classes下的class文件。  
          8.$CATALINA_HOME/server/lib/下的jar文件。  
          9.$CATALINA_BASE/shared/classes   下的class文件。  
          10.$CATALINA_BASE/shared/lib下的jar文件。  
          11.各自具體的webapp   /WEB-INF/classes下的class文件。  
          12.各自具體的webapp   /WEB-INF/lib下的jar文件。  
          class的搜尋順序如下:  
          -------------  
          Bootstrap   classes   of   your   JVM    
          System   class   loader   classses   (described   above)    
          /WEB-INF/classes   of   your   web   application    
          /WEB-INF/lib/*.jar   of   your   web   application    
          $CATALINA_HOME/common/classes    
          $CATALINA_HOME/common/endorsed/*.jar    
          $CATALINA_HOME/common/i18n/*.jar    
          $CATALINA_HOME/common/lib/*.jar    
          $CATALINA_BASE/shared/classes    
          $CATALINA_BASE/shared/lib/*.jar    
          --------------  
          因此放在不同webapp里的class文件,會被classloader加載成不同的實例。  
          例如假設下面兩個不同內容的class。分別放在不同的webapp的class目錄下。  
          package   com.lizongbo;  
          public   class   TestClass   {  
                private   String   NAME="lizongbo";  
          }  
          package   com.lizongbo;  
          public   class   TestClass   {  
                private   String   NAME="li_zongbo";  
          }  
          在不同的webapp得到的com.lizongbo.NAME結果是不同的,且互不影響。  
          但是注意,以下包名開頭的class例外:  
          javax.*    
          org.xml.sax.*    
          org.w3c.dom.*    
          org.apache.xerces.*    
          org.apache.xalan.*    
          ps,注意.在各個jar中的\META-INF\MAINFEST.MF文件里Class-Path鍵值對,也會提供jar的加載優先順序。  
          例如某jar的MAINFEST.MF內容如下:  
          Manifest-Version:   1.0  
          Created-By:   lizongbo  
          Class-Path:   commons-beanutils.jar  
          Class-Path:   commons-collections.jar  
          Class-Path:   commons-dbcp.jar  
          Class-Path:   commons-digester.jar  
          Class-Path:   commons-logging.jar  
          Class-Path:   commons-pool.jar  
          Class-Path:   commons-services.jar  
          Class-Path:   commons-validator.jar  
          Class-Path:   jakarta-oro.jar  
          Main-Class:   com.lizongbo.MyTestClass  
          那么在加載這個jar的時候,會先在此jar所在目錄下依次先加載commons-beanutils.jar,commons-collections.jar。。。等jar文件。  
          在不同的地方放置jar和class可能會產生意想不到的后果,,尤其是不同版本的jar文件,因此在實際應用部署web應用時候要特別留心.  
          例如   使用javamail常見的一個出錯信息:  
          javax.mail.NoSuchProviderException:   No   provider   for   smtp  
          其真實原因就很可能如下:  
          在不同的加載jar的目錄下放置了不同版本的mail.jar,比如一個是javamail1.3.1的mail.jar  
          在D:\jakarta-tomcat-5.5.8\common\lib下,而另外一個是javamail1.3.2的mail.jar在  
          D:\jakarta-tomcat-5.5.8\webapps\lizongbo\WEB-INF/lib下,  
          那么lizongbo這個webapp中使用到javamail進行郵件發送的時候,便會出現No   provider   for   smtp的錯誤。  
          *******************************************************************
          ClassLoader in Tomcat
          ************************************
          1 - Tomcat 的類載入器的結構


          Tomcat Server 在啟動的時候將構造一個ClassLoader樹,以保證模塊的類庫是私有的
          Tomcat Server的ClassLoader結構如下:


                  +-----------------------------+

                  |         Bootstrap           |

                  |             |               |

                  |          System             |

                  |             |               |

                  |          Common             |

                  |         /      \            |

                  |     Catalina Shared        |

                  |               /    \        |

                   |          WebApp1 WebApp2   |

                  +-----------------------------+


          其中:
          - Bootstrap - 載入JVM自帶的類和$JAVA_HOME/jre/lib/ext/*.jar
          - System - 載入$CLASSPATH/*.class
          - Common - 載入$CATALINA_HOME/common/...,它們對TOMCAT和所有的WEB APP都可見
          - Catalina - 載入$CATALINA_HOME/server/...,它們僅對TOMCAT可見,對所有的WEB APP都不可見
          - Shared - 載入$CATALINA_HOME/shared/...,它們僅對所有WEB APP可見,對TOMCAT不可見(也不必見)
          - WebApp - 載入ContextBase?/WEB-INF/...,它們僅對該WEB APP可見



          2 - ClassLoader 的工作原理


          每個運行中的線程都有一個成員contextClassLoader,用來在運行時動態地載入其它類
          系統默認的contextClassLoader是systemClassLoader,所以一般而言java程序在執行時可以使用JVM自帶的類、$JAVA_HOME/jre/lib/ext/中的類和$CLASSPATH/中的類
          可以使用 Thread.currentThread().setContextClassLoader(...); 更改當前線程的contextClassLoader,來改變其載入類的行為


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


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


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



          3 類的查找

          ClassLoader類中loadClass方法為缺省實現,用下面的順序查找類:

          1、調用findLoadedClass方法來檢查是否已經被加載。如果沒有則繼續下面的步驟。

          2、如果當前類裝載器有一個指定的委托父裝載器,則用委托父裝載器的loadClass方法加載類,也就是委托給父裝載器加載相應的類。

          3、如果這個類裝載器的委托層級體系沒有一個類裝載器加載該類,則使用類裝載器定位類的特定實現機制,調用findClass方法來查找類。



          4 - 部分原代碼分析


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

          Bootstrap中定義了三個classloader:commonLoader,catalinaLoader,sharedLoader.三者關系如下:


          // 注意三個自己定置的ClassLoader的層次關系:

                      // systemClassLoader (root)

                        //   +--- commonLoader

                      //          +--- catalinaLoader

                      //          +--- sharedLoader


          Tomcat Server 線程的起點
          構造ClassLoader樹,通過Thread.currentThread().setContextClassLoader(catalinaLoader)設置當前的classloader為catalinaLoader。
          載入若干類,然后轉入org.apache.catalina.startup.Catalina類中



          4.2 org.apache.catalina.loader.StandardClassLoader.java

          通過看loadClass這個方法來看tomcat是如何加載類的,順序如下:

          (0) Check our previously loaded class cache查找已經裝載的class
                  clazz = findLoadedClass(name);

          (1) If a system class, use system class loader通過系統classloader來裝載class

                  ClassLoader loader = system;
                      clazz = loader.loadClass(name);

          (2) Delegate to our parent if requested如果有代理則使用父類classloader

                      ClassLoader loader = parent;
                      if (loader == null)
                          loader = system;
                      clazz = loader.loadClass(name);

          (3) Search local repositories 查找本地類池,比如$CATALINA_HOME/server

                     clazz = findClass(name);

          (4) Delegate to parent unconditionally 默認使用代理裝載器



          [ 查看代碼]


          4.3 - org/apache/catalina/startup/ClassLoaderFactory.java


          根據設置創建并返回StandardClassLoader的實例


          [ 查看代碼]


          4.4 - org/apache/catalina/loader/StandardClassLoader.java


          類載入器


          4.5 - org/apache/catalina/startup/SecurityClassLoad.java


          該類僅包含一個靜態方法,用來為catalinaLoader載入一些類


          [ 查看代碼]


          Appendix - 參考


          [1] http://jakarta.apache.org/tomcat/ 中的Tomcat 4.1.x文檔Class Loader HOW-TO


          在一個 JVM 中可能存在多個 ClassLoader ,每個 ClassLoader 擁有自己的 NameSpace 。一個 ClassLoader 只能擁有一個 class 對象類型的實例,但是不同的 ClassLoader 可能擁有相同的 class 對象實例,這時可能產生致命的問題。如 ClassLoaderA ,裝載了類 A 的類型實例 A1 ,而 ClassLoaderB ,也裝載了類 A 的對象實例 A2 。邏輯上講 A1=A2 ,但是由于 A1 和 A2 來自于不同的 ClassLoader ,它們實際上是完全不同的,如果 A 中定義了一個靜態變量 c ,則 c 在不同的 ClassLoader 中的值是不同的。

          posted on 2009-07-01 18:44 石頭@ 閱讀(1011) 評論(0)  編輯  收藏 所屬分類: tomcat_core


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 拉萨市| 行唐县| 玛多县| 承德市| 卢龙县| 招远市| 积石山| 仙桃市| 罗田县| 洛南县| 正蓝旗| 无为县| 揭西县| 仙桃市| 蚌埠市| 京山县| 禹州市| 祁东县| 海宁市| 南和县| 衡水市| 法库县| 萨嘎县| 渑池县| 石柱| 察哈| 博爱县| 绥德县| 芜湖县| 玛沁县| 佳木斯市| 南郑县| 沁水县| 临高县| 内丘县| 金华市| 新昌县| 曲松县| 绥阳县| 仁怀市| 深圳市|