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

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

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

    愚人碼頭

    知恥而后勇,知不足而進(jìn)
    隨筆 - 33, 文章 - 1, 評(píng)論 - 26, 引用 - 0
    數(shù)據(jù)加載中……

    走出ClassLoader迷局 --轉(zhuǎn)至sharajava的博克http://m.tkk7.com/sharajava/archive/2006/07/25/59946.html

    : 這個(gè)問(wèn)題經(jīng)常出現(xiàn)在編寫(xiě)框架代碼 , 需要?jiǎng)討B(tài)加載很多類(lèi)和資源的時(shí)候 . 通常當(dāng)你需要?jiǎng)討B(tài)加載資源的時(shí)候 , 你至少有三個(gè) ClassLoader 可以選擇 :

    2??????? 系統(tǒng)類(lèi)加載器或叫作應(yīng)用類(lèi)加載器 (system classloader or application classloader)

    2??????? 當(dāng)前類(lèi)加載器

    2??????? 當(dāng)前線(xiàn)程類(lèi)加載器

    上面的問(wèn)題指的是最后一種類(lèi)加載器 . 哪種類(lèi)加載器是正確的選擇呢 ?

    第一種選擇可以很容易地排除 : 系統(tǒng)類(lèi)加載器 (system classloader). 這個(gè)類(lèi)加載器處理 -classpath 下的類(lèi)加載工作 , 可以通過(guò) ClassLoader.getSystemClassLoader() 方法調(diào)用 . ClassLoader 下所有的 getSystemXXX() 的靜態(tài)方法都是通過(guò)這個(gè)方法定義的 . 在你的代碼中 , 你應(yīng)該盡量少地調(diào)用這個(gè)方法 , 以其它的類(lèi)加載器作為代理 . 否則你的代碼將只能工作在簡(jiǎn)單的命令行應(yīng)用中 , 這個(gè)時(shí)候系統(tǒng)類(lèi)加載器 (system classloader) JVM 最后創(chuàng)建的類(lèi)加載器 . 一但你把代碼移到 EJB, Web 應(yīng)用或 Java Web Start 應(yīng)用中 , 一定會(huì)出問(wèn)題 .

    ????? 所以我們來(lái)看第二種選擇 : 當(dāng)前上下文環(huán)境下的類(lèi)加載器 . 根據(jù)定義 , 當(dāng)前類(lèi)加載器就是你當(dāng)前方法所屬的類(lèi)的加載器 . 在運(yùn)行時(shí)類(lèi)之間動(dòng)態(tài)聯(lián)編 , 及調(diào)用 Class.forName,() Class.getResource() 等類(lèi)似方法時(shí) , 這個(gè)類(lèi)加載器會(huì)被隱含地使用 . It is also used by syntactic constructs like X.class class literals.

    ??? 線(xiàn)程上下文類(lèi)型加載器是在Java 2平臺(tái)上被引入的. 每一個(gè)線(xiàn)程都有一個(gè)類(lèi)加載器與之對(duì)應(yīng)(除非這個(gè)線(xiàn)程是被本地代碼創(chuàng)建的). 這個(gè)類(lèi)加載器是通過(guò)Thread.setContextClassLoaser()方法設(shè)置的. 如果你不在線(xiàn)程構(gòu)造后調(diào)用這個(gè)方法, 這個(gè)線(xiàn)程將從它的父線(xiàn)程中繼承相應(yīng)的上下文類(lèi)加載器. 如果在整個(gè)應(yīng)用中你不做任何特殊設(shè)置, 所有的線(xiàn)程將都以系統(tǒng)類(lèi)加載器(system classloader)作為自己的線(xiàn)程上下文類(lèi)加載器. 自從WebJ2EE應(yīng)用服務(wù)器使用成熟的類(lèi)加載器機(jī)制來(lái)實(shí)現(xiàn)諸如JNDI, 線(xiàn)程池, 組件熱部署等功能以來(lái), 這種在整個(gè)應(yīng)用中不做任何線(xiàn)程類(lèi)加載器設(shè)置的情況就很少了.

    ??? 為什么線(xiàn)程上下文類(lèi)加載器存在于如此重要的位置呢? 這個(gè)概念在J2SE中的引入并不引人注目. 很多開(kāi)發(fā)人員對(duì)這一概念迷惑的原因是Sun公司在這方面缺乏適當(dāng)?shù)闹敢臀臋n.

    ??? 事實(shí)上, 上下文類(lèi)加載器提供了類(lèi)加載機(jī)制的后門(mén), 這一點(diǎn)也在J2SE中被引入了. 通常, JVM中的所有類(lèi)加載器被組織成了有繼承層次的結(jié)構(gòu), 每一個(gè)類(lèi)加載器(除了引導(dǎo)JVM的原始類(lèi)加載器)都有一個(gè)父加載器. 每當(dāng)被請(qǐng)示加載類(lèi)時(shí), 類(lèi)加載器都會(huì)首先請(qǐng)求其父類(lèi)加載器, 只有當(dāng)父類(lèi)加載器不能加載時(shí), 才會(huì)自己進(jìn)行類(lèi)加載.

    ?? 有時(shí)候這種類(lèi)加載的順序安排不能正常工作, 通常當(dāng)必須動(dòng)態(tài)加載應(yīng)用程序開(kāi)發(fā)人員提供的資源的時(shí)候. JNDI為例: 它的內(nèi)容(J2SE1.3開(kāi)始)就在rt.jar中的引導(dǎo)類(lèi)中實(shí)現(xiàn)了, 但是這些JNDI核心類(lèi)需要?jiǎng)討B(tài)加載由獨(dú)立廠商實(shí)現(xiàn)并部署在應(yīng)用程序的classpath下的JNDI提供者. 這種情況就要求一個(gè)父classloader(本例, 就是引導(dǎo)類(lèi)加載器)去加載對(duì)于它其中一個(gè)子classloader(本例, 系統(tǒng)類(lèi)加載器)可見(jiàn)的類(lèi). 這時(shí)通常的類(lèi)加載代理機(jī)制不能實(shí)現(xiàn)這個(gè)要求. 解決的辦法(workaround)就是, JNDI核心類(lèi)使用當(dāng)前線(xiàn)程上下文的類(lèi)加載器, 這樣, 就基本的類(lèi)加載代理機(jī)制的相反方向建立了一條有效的途徑.

    ??? 另外, 上面一段可能讓你想起一些其它的事情: XML解析Java API(JAXP). 是的, 當(dāng)JAXP只是J2SE的擴(kuò)展進(jìn), 它很自然地用當(dāng)前類(lèi)加載器來(lái)引導(dǎo)解析器的實(shí)現(xiàn). 而當(dāng)JAXP被加入到J2SE1.4的核心類(lèi)庫(kù)中時(shí), 它的類(lèi)加載也就改成了用當(dāng)前線(xiàn)程類(lèi)加載器, JNDI的情況完全類(lèi)似(也使很多程序員很迷惑). 明白為什么我說(shuō)來(lái)自Sun的指導(dǎo)很缺乏了吧?

    ?? 在以上的介紹之后, 我們來(lái)看關(guān)鍵問(wèn)題: 這兩種選擇(當(dāng)前類(lèi)加載器和當(dāng)前線(xiàn)程類(lèi)加載器)都不是在所有環(huán)境下都適用. 有些人認(rèn)為當(dāng)前線(xiàn)程類(lèi)加載器應(yīng)該成為新的標(biāo)準(zhǔn)策略. 但是, 如果這樣, 當(dāng)多個(gè)線(xiàn)程通過(guò)共享數(shù)據(jù)進(jìn)行交互的時(shí), 將會(huì)呈現(xiàn)出一幅極其復(fù)雜的類(lèi)加載的畫(huà)面, 除非它們?nèi)渴褂昧送粋€(gè)上下文的類(lèi)加載器. 進(jìn)一步說(shuō), 在某些遺留下來(lái)的解決方案中, 委派到當(dāng)前類(lèi)加載器的方法已經(jīng)是標(biāo)準(zhǔn). 比如對(duì)Class.forName(String)的直接調(diào)用(這也是我為什么推薦盡量避免對(duì)這個(gè)方法進(jìn)行調(diào)用的原因). 即使你努力去只調(diào)用上下文相關(guān)的類(lèi)加載器, 仍然會(huì)有一些代碼會(huì)不由你控制. 這種不受控制的類(lèi)加載委派機(jī)制是混入是很危險(xiǎn)的.

    ??? 更嚴(yán)重的問(wèn)題, 某些應(yīng)用服務(wù)器把環(huán)境上下文及當(dāng)前類(lèi)加載器設(shè)置到不同的類(lèi)加載器實(shí)例上, 而這些類(lèi)加載器有相同的類(lèi)路徑但卻沒(méi)有委派機(jī)制中的父子關(guān)系. 想想這為什么十分可怕. 要知道類(lèi)加載器定義并加載的類(lèi)實(shí)例會(huì)帶有一個(gè)JVM內(nèi)部的ID號(hào). 如果當(dāng)前類(lèi)加載器加載一個(gè)類(lèi)X的實(shí)例, 這個(gè)實(shí)例調(diào)用JNDI查找類(lèi)Y的實(shí)例, 些時(shí)的上下文的類(lèi)加載器也可以定義了加載類(lèi)Y實(shí)例. 這個(gè)類(lèi)Y的定義就與當(dāng)前類(lèi)加載器看到的類(lèi)Y的定義不同. 如果進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換, 則產(chǎn)生異常.

    ?? 這種混亂的情況還將在Java中存在一段時(shí)間. 對(duì)于那些需要?jiǎng)討B(tài)加載資源的J2SEAPI, 我們來(lái)猜想它們的類(lèi)加策略. 例如:

    ????????? JNDI 使用線(xiàn)程上下文類(lèi)加載器

    ????????? Class.getResource() Class.forName()使用當(dāng)前類(lèi)加載器

    ????????? JAXP(J2SE 1.4 及之后)使用線(xiàn)程上下文類(lèi)加載器

    ????????? java.util.ResourceBundle 使用調(diào)用者的當(dāng)前類(lèi)加載器

    ????????? URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only

    ????????? Java 序列化API默認(rèn)使用調(diào)用者當(dāng)前的類(lèi)加載器

    這些類(lèi)及資源的加載策略問(wèn)題, 肯定是J2SE領(lǐng)域中文檔最及說(shuō)明最缺乏的部分了.

    posted on 2006-07-27 15:23 船夫 閱讀(312) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): java技術(shù)

    主站蜘蛛池模板: 亚洲国产综合在线| 国产亚洲人成在线影院| 免费无码黄网站在线观看| 五月天国产成人AV免费观看| 亚洲视频一区调教| 四虎影在线永久免费四虎地址8848aa| 女人隐私秘视频黄www免费| 国产色在线|亚洲| 国产亚洲自拍一区| 成人片黄网站A毛片免费| 国产乱子伦精品免费视频| 亚洲国产成人久久综合一区| 亚洲人成国产精品无码| 在线观看免费人成视频色9| 国产免费AV片在线观看播放| 国产精品亚洲片在线va| 久久九九亚洲精品| 免费人成视频在线观看不卡| 在线观看免费中文视频| 午夜在线免费视频 | 亚洲精品无码久久久久去q| 青青草国产免费久久久下载| 香港a毛片免费观看 | 精品国产福利尤物免费| 色婷五月综激情亚洲综合| 亚洲AV无码日韩AV无码导航| 免费国产在线观看老王影院| 国产福利在线免费| 久久国产免费一区| 成人无码视频97免费| 日韩亚洲人成在线综合| 67194在线午夜亚洲| 亚洲最大的成网4438| 亚洲国产一二三精品无码| 亚洲成?Ⅴ人在线观看无码| 精品国产麻豆免费网站| 中文字幕无码免费久久99| 在线人成精品免费视频| 日本免费污片中国特一级| 免费久久人人爽人人爽av| 国产男女爽爽爽免费视频|