隨筆-72  評(píng)論-20  文章-0  trackbacks-1
          1         問題背景
          我們都知道,Java平臺(tái)一大亮點(diǎn)就在于其類裝載器體系結(jié)構(gòu),這使得JVM可以在運(yùn)行期從Java API,擴(kuò)展路經(jīng)(java.ext.path),classpath以及用戶指定的位置(文件或網(wǎng)絡(luò))中載入所需的class,從而達(dá)到動(dòng)態(tài)裝載的目的。然而其類裝載器委托模型在保證了安全性和強(qiáng)大功能的同時(shí),也導(dǎo)致了相當(dāng)?shù)膹?fù)雜性,有很多地方一旦我們不加注意的話就將導(dǎo)致錯(cuò)誤。這里我希望通過一些小例子來展示動(dòng)態(tài)裝載的某些方面,深入地了解一下怎么進(jìn)行動(dòng)態(tài)裝載,會(huì)遇到什么樣的問題,并就問題的原因與解決方法進(jìn)行討論。
          也許有人會(huì)說:我的程序不用什么動(dòng)態(tài)裝載,平時(shí)運(yùn)行程序就是java –classpath … myPkg.my不就行了嗎?不過我總是聽前輩說,要想完全了解一個(gè)系統(tǒng)下的程序設(shè)計(jì),就必須要深入研究這個(gè)平臺(tái)的特性,做到心中有數(shù),做起程序才不會(huì)處處制肘。類的動(dòng)態(tài)裝載是Java平臺(tái)最顯著的特征之一,許多著名的項(xiàng)目—Tomcat,Eclipse都使用自定義類裝載器來裝載運(yùn)行時(shí)所需的類,如果連什么是動(dòng)態(tài)裝載和怎么動(dòng)態(tài)裝載都沒弄明白,還能去玩自定義類裝載器嗎?所以還是不要浮躁,讓我們從基礎(chǔ)開始做起吧。
          不過,我要說明的是你在看我這篇文章之前最好先熟悉一下Java的類裝載器體系結(jié)構(gòu)以及其委托模型,我在這里將不加贅述,推薦先看《Java深度歷險(xiǎn)》或《深入Java虛擬機(jī)》的第八章。
          2         問題研究
          我要討論的問題是:讓一個(gè)在my/目錄下名為my.Main的類在運(yùn)行期讀取util/中的util.Tool類。
          l      實(shí)驗(yàn)一
          我的實(shí)驗(yàn)?zāi)夸浗Y(jié)構(gòu)如下
          其中my.Main的代碼如下:
          util.Tool的代碼如下:
          在my.Main的代碼中16行可以看到,我定義了一個(gè)URLClassLoader,想要讀取在同一根目錄下的/util/Tool.class,接下來我們運(yùn)行如下命令,來查看運(yùn)行結(jié)果:
          如上可見,util.Tool被順利裝載了,可是裝載它的卻是AppClassLoader,而不是我們想要的URLClassLoader!這是為什么呢?
          原因很簡(jiǎn)單,因?yàn)槲覀儗til/Tool.class放到了JVM系統(tǒng)變量”user.dir”所指定的目錄下,而該JVM系統(tǒng)變量的默認(rèn)值為程序所在的根目錄,相當(dāng)于把classpath 設(shè)置成了”.”,也就是相當(dāng)于執(zhí)行了命令:
          java –classpath . my.Main
          于是,正因?yàn)閡til.Tool在classpath下,于是JVM就在在載入my.Main的同時(shí)也用AppClassLoader將util.Tool載入到JVM了。我們可以通過如下命令看得更清楚一些:
          java –verbose my.Main
          看到了嗎?由于AppClassLoader預(yù)先載入了util.Tool類,而且在URLClassLoader的loadClass()方法中有如下語(yǔ)句:Class cls = getLoadedClass(String className);如果cls!=null的話,loadClass()就會(huì)返回已經(jīng)載入過的(即由AppClassLoader所載入的)util.Tool類,所以我們才會(huì)看到以上結(jié)果。
          l      實(shí)驗(yàn)二
          為了解決以上問題,我決定按如下方式組織我的目錄結(jié)構(gòu)
          然后對(duì)Main.class進(jìn)行一些修改:
          嗯,這下我們不會(huì)為util.Tool被提前載入而煩惱了,因?yàn)檩d入my.Main的AppClassLoader找不到換了位置的util.Tool類了。這回可能有人會(huì)問:subdir不是還在”user.dir”所指向的目錄下嗎?怎么只是多建了一層目錄它就找不到了呢?原因是AppClassLoader是URLClassLoader的子類,它在載入class的時(shí)候也是根據(jù)該類的URL來進(jìn)行定位的。那我們的例子來說,在“實(shí)驗(yàn)一”的最后一個(gè)圖中我們可以看到,util.Tool被載入的位置為:file1:c:/Test1,JVM得到這個(gè)URL之后,將util.Tool中的”.”變?yōu)?#8221;/”,在所得字符串結(jié)尾處加上”.class”,最后將該字符串加到file1:c:/Test1后面,就得到了最終util.Tool的URL:file1:c:/Test1/util/Tool.class。而當(dāng)加了一層subdir目錄之后,AppClassLoader就找不到file1:c:/Test1/util/Tool.class了,而只能由程序中定義的URLClassLoader來載入。
           這時(shí)我們運(yùn)行程序,所得結(jié)果如下:
          可以看到,util.Tool確實(shí)已經(jīng)我定義的URLClassLoader被載入并初始化了,可是為什么最后卻有一個(gè)奇怪的異常呢?NoClassDefFoundError?util.Tool類不是都載入了嗎?怎么還找不到它的定義呢?
          這個(gè)問題需要從JVM的裝載機(jī)制說起。對(duì)于每個(gè)JVM實(shí)例,除了內(nèi)置的三個(gè)內(nèi)置的類裝載器(BootStrapClassLoader,ExtClassLoader,AppClassLoader)之外,還可能有一些用戶自定義的類裝載器,而這些類裝載器可能在一個(gè)程序運(yùn)行周期內(nèi)載入同一個(gè)類,即AppClassLoader和我自定義的類裝載器MyClassLoader可能同時(shí)需要載入util.Tool類。為了避免沖突,JVM為每個(gè)類裝載器都建立一個(gè)命名空間,并以此作為其訪問邊界。例如,如果my.Main想要引用util.Tool類,則它們必須是由同一裝載器載入的,也就是說,由于my.Main是由AppClassLoader載入的,那么util.Tool也必須由AppClassLoader載入才行,否則就會(huì)出現(xiàn)上述錯(cuò)誤。
          問題解決的方法很簡(jiǎn)單,只需要讓my.Main和util.Tool由同一類裝載器載入就行了。
          l      實(shí)驗(yàn)三
          為了讓my.Main和util.Tool由同一類裝載器載入,我定義了一個(gè)啟動(dòng)類—boot.BootStrap類:
          在這里,我用自己的類裝載器ucl來裝載my.Main類,并用reflection API來調(diào)用其main()方法,同時(shí)也對(duì)my.Main稍作修改
          然后執(zhí)行一下看看結(jié)果:
          圖中可以很清楚的看到BootStrap類則是由AppClassLoader載入的,而Main類和Tool類都是由同一個(gè)URLClassLoader在運(yùn)行期動(dòng)態(tài)載入的。至此,我們完成了對(duì)類的動(dòng)態(tài)載入的全部說明和實(shí)驗(yàn),希望對(duì)你能有所幫助。 
          posted on 2007-07-27 22:28 前方的路 閱讀(365) 評(píng)論(1)  編輯  收藏 所屬分類: Java技術(shù)

          評(píng)論:
          # re: 動(dòng)態(tài)裝載問題的研究 2012-03-29 11:21 | yangzg
          似懂非懂  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 合作市| 新泰市| 清原| 新平| 泾源县| 安塞县| 临桂县| 吉木萨尔县| 涪陵区| 寻甸| 旌德县| 罗田县| 确山县| 舒兰市| 建德市| 新田县| 金乡县| 武鸣县| 临桂县| 临沂市| 化隆| 威远县| 贵定县| 利津县| 和平区| 阳朔县| 富阳市| 湟中县| 安徽省| 通渭县| 云和县| 呈贡县| 海淀区| 南靖县| 咸阳市| 建平县| 旬阳县| 霍城县| 额尔古纳市| 德州市| 临沭县|