sharajava

          走出ClassLoader迷局

          : 我什么時(shí)候應(yīng)該使用 Thread.getContextClassLoader()?

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

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

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

          2??????? 當(dāng)前線程類加載器

          上面的問題指的是最后一種類加載器 . 哪種類加載器是正確的選擇呢 ?

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

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

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

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

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

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

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

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

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

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

          ????????? JNDI 使用線程上下文類加載器

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

          ????????? JAXP(J2SE 1.4 及之后)使用線程上下文類加載器

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

          ????????? 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)前的類加載器

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

          posted on 2006-07-25 11:04 sharajava 閱讀(2508) 評論(1)  編輯  收藏 所屬分類: 深入Java底層

          評論

          # re: 走出ClassLoader迷局 2008-05-30 10:53 ceshi

          樓主的意思是不是提倡使用 當(dāng)前上下文的類裝載器(當(dāng)前線程類加載器)?  回復(fù)  更多評論   


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 郯城县| 汽车| 黄陵县| 六枝特区| 历史| 临安市| 聂荣县| 盐源县| 黑龙江省| 宁化县| 墨竹工卡县| 郓城县| 高要市| 隆昌县| 清远市| 望城县| 浏阳市| 顺昌县| 新密市| 牙克石市| 丹凤县| 堆龙德庆县| 虎林市| 闸北区| 两当县| 韩城市| 广德县| 二手房| 星子县| 水富县| 潜江市| 安达市| 安吉县| 阿图什市| 沛县| 桐乡市| 屯留县| 玉环县| 镇江市| 兴安盟| 清苑县|