<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

      Sparta Yew

         簡約、職業、恒久
    隨筆 - 15, 文章 - 1, 評論 - 276, 引用 - 0
    數據加載中……

    Weblogic與Java類加載器原理試驗解析


    sparta-紫杉 2011-4-12 16:47

    通過試驗,得出一個結論, 假設在Weblogic的Server/lib下有一個類,與應用的Webapp/WEB-INF/classes下的類名相同,方法名也相同,僅有在后臺打印出來的字符的稍許差別,那在Weblogic啟動后,無論個文件夾中的類誰是新編譯的(版本新或舊),應用系統均默認是使用server/lib下的類,而不是引用Webapp/WEB-INF/classes下的類。

    一、通過翻閱大量的資料了解到,java類加載的原理如下

    JVM在運行時會產生三個ClassLoader:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.其中,Bootstrap是用C++編寫的,
    我們在Java中看不到它,是null,它用來加載核心類庫。

    關于Bootstrap ClassLoader,在JVM源代碼中這樣寫道:
    static const char classpathFormat[] =
    "%/lib/rt.jar: "
    "%/lib/i18n.jar: "
    "%/lib/sunrsasign.jar: "
    "%/lib/jsse.jar: "
    "%/lib/jce.jar: "
    "%/lib/charsets.jar: "
    "%/classes ";
    知道為什么不需要在classpath中加載這些類了吧?人家在JVM啟動的時候就自動加載了,并且在運行過程中根本不能修改Bootstrap加載路徑。
    Extension ClassLoader用來加載擴展類,即/lib/ext中的類。
    最后AppClassLoader才是加載Classpath的。
    ClassLoader加載類用的是委托模型。即先讓Parent類(而不是Super,不是繼承關系)尋找,Parent找不到才自己找??磥鞢lassLoader還是蠻孝順的。三者的關系為:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent為Bootstrap ClassLoader。加載一個類時,首先BootStrap先進行尋找,找不到再由ExtClassLoader尋找,最后才是AppClassLoader。
    為什么要設計的這么復雜呢?其中一個重要原因就是安全性。比如在Applet中,如果編寫了一個java.lang.String類并具有破壞性。假如不采用這種委托機制,就會將這個具有破壞性的String加載到了用戶機器上,導致破壞用戶安全。但采用這種委托機制則不會出現這種情況。因為要加載java.lang.String類時,系統最終會由Bootstrap進行加載,這個具有破壞性的String永遠沒有機會加載。

    有一篇文章完整詮釋了Java類加載的原理,參見如下:

    ---------------------------------------------------引用開始--------------------------------------------------------

    《我對java中類裝載的理解 》

    1.Java中的所有類,必須被裝載到jvm中才能運行,這個裝載工作是由jvm中的類裝載器完成的, 
    類裝載器所做的工作實質是把類文件從硬盤讀取到內存中。 

    2.java中的類大致分為三種: 
      1.系統類 
      2.擴展類 
      3.由程序員自定義的類 

    3.類裝載方式,有兩種 
      1.隱式裝載, 程序在運行過程中當碰到通過new 等方式生成對象時,隱式調用類裝載器加載對應的類到jvm中, 
      2.顯式裝載, 通過class.forname()等方法,顯式加載需要的類 
      隱式加載與顯式加載的區別: 
      兩者本質是一樣?  ? 

    4.類加載的動態性體現 
      一個應用程序總是由n多個類組成,Java程序啟動時,并不是一次把所有的類全部加載后再 
    運行,它總是先把保證程序運行的基礎類一次性加載到jvm中,其它類等到jvm用到的時候再加載,這樣的好處是節省了內存的開銷,因為java最早就是為嵌入式系統而設計的,內存寶貴,這是一種可以理解的機制,而用到時再加載這也是java動態性的一種體現 

    5.java類裝載器 
      Java中的類裝載器實質上也是類,功能是把類載入jvm中,值得注意的是jvm的類裝載器并不是一個,而是三個,層次結構如下: 
      Bootstrap Loader - 負責加載系統類 
      | 
      - - ExtClassLoader - 負責加載擴展類 
      | 
      - - AppClassLoader - 負責加載應用類 
      為什么要有三個類加載器,一方面是分工,各自負責各自的區塊,另一方面為了實現委托模型,下面會談到該模型
     
    6. 類加載器之間是如何協調工作的 
      前面說了,java中有三個類加載器,問題就來了,碰到一個類需要加載時,它們之間是如何協調工作的,即java是如何區分一個類該由哪個類加載器來完成呢。 
    在這里java采用了委托模型機制,這個機制簡單來講,就是“類裝載器有載入類的需求時,會先請示其Parent使用其搜索路徑幫忙載入,如果Parent 找不到,那么才由自己依照自己的搜索路徑搜索類”,注意喔,這句話具有遞歸性。
     
    下面舉一個例子來說明,為了更好的理解,先弄清楚幾行代碼: 
    Public class Test{ 
      Public static void main(String[] arg){ 
      ClassLoader c = Test.class.getClassLoader(); //獲取Test類的類加載器 
      System.out.println(c);  
      ClassLoader c1 = c.getParent(); //獲取c這個類加載器的父類加載器 
      System.out.println(c1); 
      ClassLoader c2 = c1.getParent();//獲取c1這個類加載器的父類加載器 
      System.out.println(c2); 
      } 

    把以上代碼存到d:\my 文件夾下,直接編譯,然后在dos模式下運行 
    D:\my\java Test 
      。。。AppClassLoader。。。 
      。。。ExtClassLoader。。。 
      Null 

    D:\my 

    注: 。。。表示省略了內容 
    可以看出Test是由AppClassLoader加載器加載的 
    AppClassLoader的Parent 加載器是 ExtClassLoader 

    但是ExtClassLoader的Parent為 null 是怎么回事呵,朋友們留意的話,前面有提到Bootstrap Loader是用C++語言寫的,依java的觀點來看,邏輯上并不存在Bootstrap Loader的類實體,所以在java程序代碼里試圖打印出其內容時,我們就會看到輸出為null。
     
    【注:以下內容大部分引用java深度歷險】 
    弄明白了上面的示例,接下來直接進入類裝載的委托模型實例,寫兩個文件,如下: 
    文件:Test1.java 
    Public class Test1{ 
      Public static void main(String[] arg){ 
      System.out.println(Test1.class.getClassLoader()); 
      Test2 t2 = new Test2(); 
      T2.print(); 
      } 

    文件: Test2.java 
    Public class Test2{ 
      Public void prin(){ 
      System.out.println(this.getClass().getClassLoader()); 
      } 

    這兩個類的作用就是打印出載入它們的類裝載器是誰, 將這兩個文件保存到d:\my目錄下,編譯后,我們在復制兩份,分別置于jdk1.4\jre\classes下(注意,剛開始我們的系統下沒有此目錄,需自己建立) 與 jdk1.4\jre\lib\ext\classes下(同樣注意,開始我們的系統下也沒此目錄,手工建立), 然后切換到d:\my目錄下開始測試, 

    測試一: 
    <JRE所在目錄>\classes下 
    Test1.class 
    Test2.class 

    <JRE所在目錄>\lib\ext\classes下 
    Test1.class 
    Test2.class 

    D:\my下 
    Test1.class 
    Test2.class 


    dos下輸入運行命令,結果如下: 
    D:\my>java Test1 
    Null 
    Null 

    D:\my> 
       
      從輸出結果我們可以看出,當AppClassLoader要載入Test1.class時,先請其Parent,也就是ExtClassLoader來載入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class. 由于 <JRE所在目錄>\Classes目錄為Bootstrap Loader的搜索路徑之一,所以Bootstrap Loader找到了Test1.class,因此將它載入,接著在Test1.class之內有載入Test2.class的需求,由于Test1.class是由Bootstrap Loader所載入,所以Test2.class內定是由Bootstrap Loader根據其搜索路徑來找,因Test2.class也位于Bootstrap Loader可以找到的路徑下,所以也被載入了,最后我們看到Test1.class與Test2.class都是由Bootstrap Loader(null)載入。 


    測試二: 
    <JRE所在目錄>\classes下 
    Test1.class 

    <JRE所在目錄>\lib\ext\classes下 
    Test1.class 
    Test2.class 

    D:\my下 
    Test1.class 
    Test2.class 

    dos下輸入運行命令,結果如下: 
    D:\my>java Test1 
    Null 
    Exception in thread “main” java.lang.NoClassdefFoundError:Test2 at Test1.main。。。 
    D:\my> 

      從輸出結果我們可以看出,當AppClassLoader要載入Test1.class時,先請其Parent,也就是ExtClassLoader來載入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class. 由于 <JRE所在目錄>\Classes目錄為Bootstrap Loader的搜索路徑之一,所以Bootstrap Loader找到了Test1.class,因此將它載入,接著在Test1.class之內有載入Test2.class的需求,由于Test1.class是由Bootstrap Loader所載入,所以Test2.class內定是由Bootstrap Loader根據其搜索路徑來找,但是因為Bootstrap Loader根本找不到Test2.class(被我們刪除了),而Bootstrap Loader又沒有Parent,所以無法載入Test2.class.最后我們看到Test1.class是由Bootstrap Loader(null)載入,而Test2.class則無法載入 


    測試三 
    <JRE所在目錄>\classes下 

    Test2.class 

    <JRE所在目錄>\lib\ext\classes下 
    Test1.class 
    Test2.class 

    D:\my下 
    Test1.class 
    Test2.class 

    dos下輸入運行命令,結果如下: 
    D:\my>java Test1 
    。。。ExtClassLoader。。。 
    Null 

    D:\my> 

      從輸出結果我們可以看出,當AppClassLoader要載入Test1.class時,先請其Parent,也就是ExtClassLoader來載入,而ExtclassLoader又請求其Parent,即Bootstrap Loader來載入Test1.class.但是Bootstrap Loader無法在其搜索路徑下找到Test1.class(被我們刪掉了),所以ExtClassLoader只得自己搜索,因此ExtClassLoader在其搜索路徑 <JRE所在目錄>\lib\ext\classes下找到了Test1.class,因此將它載入,接著在Test1.class之內有載入Test2.class的需求,由于Test1.class是由ExtClassLoader所載入,所以Test2.class內定是由ExtClassLoader根據其搜索路徑來找,但是因為ExtClassLoader有Parent,所以先由Bootstrap Loader幫忙尋找,Test2.class位于Bootstrap Loader可以找到的路徑下,所以被Bootstrap Loader載入了.最后我們看到Test1.class是由ExtClassLoader載入,而Test2.class則是由Bootstrap Loader(null)載入 

      了解了以上規則,請朋友們自行分析以下場景的執行結果 

    測試四: 
    <JRE所在目錄>\classes下 


    <JRE所在目錄>\lib\ext\classes下 
    Test1.class 
    Test2.class 

    D:\my下 
    Test1.class 
    Test2.class 


    測試五: 
    <JRE所在目錄>\classes下 


    <JRE所在目錄>\lib\ext\classes下 
    Test1.class 


    D:\my下 
    Test1.class 
    Test2.class 


    測試六: 
    <JRE所在目錄>\classes下 


    <JRE所在目錄>\lib\ext\classes下 

    Test2.class 


    D:\my下 
    Test1.class 
    Test2.class 


    測試七: 
    <JRE所在目錄>\classes下 


    <JRE所在目錄>\lib\ext\classes下 


    D:\my下 
    Test1.class 
    Test2.class 

    ---------------------------------------------------引用結束,感謝作者哈!--------------------------------------------------------

    上述經過自己的猜測,并進行實地測試,發現確實有一定的道理。


    二、Weblogic對于類加載的原理

    容器也對類加載進行了封裝, 不過不同的容器對于類加載有所不同。 在Weblogic中,是這樣的原理。

    Weblogic中classloader是分層次的,它只能加載比它層次高的類及它自身的類,同層次的類及比它層次低的類都不能加載。

    在weblogic中的classloader有5個層次,從高到低排:
    a. jdk
    b. jdk ext
    c. system classpath
    d. ( APP-INF/classes  and  APP-INF/lib )
    e. ( WEB-INF/classes and WEB-INF/lib )
        注意:這里是先加載classes中的類,再加載lib中的類, 若要修改它的加載順序,可以通過在Weblogic.xml(版本為8)中加入以下代碼:
        <container-descriptor>
           <prefer-web-inf-classes> true </prefer-web-inf-classes>
        </container-descriptor>

    f. ejb.jar
    注意:e 和 f 的classloader是同級的。
    所以APP-INF/lib和APP-INF/classes下類不能實例化webapp下的類,這點尤其要注意,否則會報類找不到的錯誤。

    還有一篇網文《Weblogic10 Classloading 問題》比較詳細地介紹了Weblogic加載類的原理。

    來自Weblogic官方的說明文件中,對于Weblogic的類加載順序給出了一個比較清晰和簡單的描述:

     當部署一個應用的時候,weblogic server會自動創建一個具有層次結構的類裝載器。
      1、a.Application Classloader負責裝載應用中的所有的EJB JAR文件;
      2、b.Web Application Classloader負責裝載所有的Web application 中的WAR 文件(所有得jsp文件除外);
      3、c.Jsp Classloader 負責裝載Web application 中的所有的jsp 文件。


        Tomcat與Weblogic是相反的:對于運行在 Java EE 容器中的 Web 應用來說,類加載器的實現方式與一般的 Java 應用有所不同。不同的 Web 容器的實現方式也會有所不同。以 Apache Tomcat 來說,每個 Web 應用都有一個對應的類加載器實例。該類加載器也使用代理模式,所不同的是它是首先嘗試去加載某個類,如果找不到再代理給父類加載器。這與一般類加載器的順序是相反的。這是 Java Servlet 規范中的推薦做法,其目的是使得 Web 應用自己的類的優先級高于 Web 容器提供的類。這種代理模式的一個例外是:Java 核心庫的類是不在查找范圍之內的。這也是為了保證 Java 核心庫的類型安全。


        最后,總結一下,在Weblogic服務啟動的過程中,自動形成一個具有層次結構的類裝載器,首先裝載jdk及java擴展jar包或類;然后再加載Weblogic本身使用的各個jar包或類;然后再加載Web應用文件夾里面的classes下的類,然后再加載Web應用文件夾里面lib下的jar包或類。也就是說,每個層次的類裝載器均對應不同的類路徑,它們是一一對應的。 比如System裝載器對應著jdk及擴展路徑;Application裝載器對應著Weblogic的相關類;而web 應用裝載器對應著webapp應用下的classes和lib下的路徑;而jsp裝載器則對應著jsp文件。

        當然,在加載過程中,若在高層次的加載器中已經加載了某類,那么再以后的加載中,再次遇到該類也不會加載,只是會忽略。加載完成之后,將類放入Cache中供系統應用調用。

        在系統的運行過程中,若遇到使用該類的情況,則會遵循先通過其父類加載器進行加載的原則,比方說,我要加載一個WSWordManager類,
    則系統會首先在Cache中尋找,若找不到,則調用父裝載器到與之對應的路徑里面去尋找,一直向上,找著了則進行加載,若找不著則報出ClassNotFound的異常。

        哈哈,自己耗時一天的試驗也能夠證明了這一點。

        在解決系統項目“原生類重復加載,異常為jacob.dll already loaded in another classloader”問題時,被迫研究了上述的原理。現在問題也算是較完美的解決了,更關鍵的是學到了很多相關底層的知識,對于自己技術的提升是很有好處的。



                -東營 sparta-紫杉 原創,轉載請注明出處 :)
                http://www.tkk7.com/SpartaYew/
                SpartaYew@163.com
     
                
    QQ:22086526

    posted on 2011-05-18 16:34 sparta-紫杉 閱讀(5318) 評論(1)  編輯  收藏 所屬分類: Java

    評論

    # re: Weblogic與Java類加載器原理試驗解析  回復  更多評論   

    樓主 不對把 如果項目中存在與jar包相同的類(包名.類名) 則首先優先加載的是class中類 而不是lib中的jar包中的類,擴充框架功能時我都是用這種方法來實現覆蓋掉框架jar包中的的類來測試的
    2012-01-10 23:54 | 叟策
    主站蜘蛛池模板: 在线涩涩免费观看国产精品| 成人午夜免费视频| 日本黄色动图免费在线观看| 亚洲视频在线精品| 特级毛片A级毛片100免费播放| 在线观看成人免费| 亚洲爆乳AAA无码专区| 成年在线网站免费观看无广告| 亚洲另类自拍丝袜第1页| 91精品免费久久久久久久久| 亚洲熟妇av一区| 全免费毛片在线播放| 亚洲视频在线观看| 18禁男女爽爽爽午夜网站免费| 久久久亚洲欧洲日产国码二区| 国产偷伦视频免费观看| 久久国产精品亚洲一区二区| 亚洲视频免费在线观看| 伊人久久综在合线亚洲2019| 99久久国产免费-99久久国产免费| 亚洲精品资源在线| 四虎国产精品免费久久| 亚洲精品无码久久| 免费一级成人毛片| 国产啪精品视频网站免费尤物| 亚洲精品国产成人99久久| 黄在线观看www免费看| 亚洲色偷偷综合亚洲av78 | 中文字幕无码播放免费| 亚洲精品无码久久| 亚洲综合区小说区激情区| 国内精品久久久久影院免费| 亚洲第一区视频在线观看| 免费的涩涩视频在线播放| 国产精品免费视频观看拍拍| 亚洲麻豆精品果冻传媒| 免费欧洲毛片A级视频无风险| 三年在线观看免费观看完整版中文| 亚洲高清美女一区二区三区| 四虎免费永久在线播放| 三年片在线观看免费观看大全一|