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

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

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

    迷途書童

    敏感、勤學(xué)、多思
    隨筆 - 77, 文章 - 4, 評論 - 86, 引用 - 0
    數(shù)據(jù)加載中……

    通過ClassLoader管理組件依賴

    Java的類加載機制是非常強大的。你可以利用外部第三方的組件而不需要頭文件或靜態(tài)連接。你只需要簡單的把組件的JAR文件放到classpath下的目錄中。運行時引用完全是動態(tài)處理的。但如果這些第三方組件有自己的依賴關(guān)系時會怎么樣呢?通常這需要開發(fā)人員自己解決所有需要的相應(yīng)版本的組件集,并且確認(rèn)他們被加到classpath中。
      
      JAR清單文件
      
      實際上你不需要這樣做,Java的類加載機制可以更優(yōu)雅地解決這個問題。一種方案是需要每一個組件的作者在JAR清單中定義內(nèi)部組件的依賴關(guān)系。這里清單是指一個被包含在JAR中的定義文件元數(shù)據(jù)的文本文件(META-INF/MANIFEST.MF)。最常用的屬性是Main-Class,定義了通過java –jar方式定位哪個類會被調(diào)用。然而,還有一個不那么有名的屬性Class-Path可以用來定義他所依賴的其他JAR。Java缺省的ClassLoader會檢查這些屬性并且自動附加這些特定的依賴到classpath中。
      
      讓我們來看一個例子??紤]一個實現(xiàn)交通模擬的Java應(yīng)用,他由三個JAR組成:
      
      ·simulator-ui.jar:基于Swing的視圖來顯示模擬的過程。
      ·simulator.jar:用來表示模擬狀態(tài)的數(shù)據(jù)對象和實現(xiàn)模擬的控制類。
      ·rule-engine.jar:常用的第三方規(guī)則引擎被用來建立模擬規(guī)則的模型。
      simulator-ui.jar依賴simulator.jar,而simulator.jar依賴rule-engine.jar。
      
      而通常執(zhí)行這個應(yīng)用的方法如下:
      $ java -classpath
      simulator-ui.jar:simulator.jar:rule-engine.jar
      com.oreilly.simulator.ui.Main
      
      編者注:上面的命令行應(yīng)該在同一行鍵入;只是由于網(wǎng)頁布局的限制看起來好像是多行。
      
      但我們也可以在JAR的清單文件中定義這些信息,simulator-ui的MANIFEST.MF如下:
      
      Main-Class: com.oreilly.simulator.ui.Main
      Class-Path: simulator.jar
      
      而simulator的MANIFEST.MF包含:
      Class-Path: rule-engine.jar
      
      rule-engine.jar或者沒有清單文件,或者清單文件為空。
      
      現(xiàn)在我們可以這樣做:
      $ java -jar simulator-ui.jar
      
      Java會自動解析清單的入口來取得主類及修改classpath,甚至可以確定simulator-ui.jar的路徑和解釋所有與這個路徑相關(guān)的Class-Path屬性,所以我們可以簡單按照下面的方式之一來做:
      $ java -jar ../simulator-ui.jar
      $ java -jar /home/don/build/simulator-ui.jar
      
      依賴沖突
      
      Java的Class-Path屬性的實現(xiàn)相對于手工定義整個classpath是一個大的改善。然而,兩種方式都有自己的限制。一個重要的限制就是你只能加載組件的一個特定版本。這看起來是很顯然的因為許多編程環(huán)境都有這個限制。但是在大的包含多個第三方依賴的多JAR項目中依賴沖突是很常見的。
      
      例如,你正在開發(fā)一個通過查詢多個搜索引擎并比較他們的結(jié)果的搜索引擎。Google和Amazon的Alexa都支持使用SOAP作為通訊機制的網(wǎng)絡(luò)服務(wù)API,也都提供了相應(yīng)的Java類庫方便訪問這些API。讓我們假設(shè)你的JAR- metasearch.jar,依賴于google.jar和amazon.jar,而他們都依賴于公共的soap.jar。
      
      現(xiàn)在是沒有問題,但如果將來SOAP協(xié)議或API發(fā)生改變時會怎么樣呢?很可能這兩個搜索引擎不會選擇同時升級??赡茉谀骋惶炷阍L問Amazon時需要SOAP1.x版本而訪問Google時需要SOAP2.x版本,而這兩個版本的SOAP并不能在同一個進程空間中共存。在這里,我們可能包含下面的JAR依賴:
      
      $ cat metasearch/META-INF/MANIFEST.MF
      Main-Class: com.onjava.metasearch.Main
      Class-Path: google.jar amazon.jar
      
      $ cat amazon/META-INF/MANIFEST.MF
      Class-Path: soap-v1.jar
      
      $ cat google/META-INF/MANIFEST.MF
      Class-Path: soap-v2.jar
      
      上面正確地描述了依賴關(guān)系,但這里并沒有包含什么魔法--這樣設(shè)置并不會像我們期望地那樣工作。如果soap-v1.jar和soap-v2.jar定義了許多相同的類,我們肯定這是會出問題的。
      $ java -jar metasearch.jar
      SOAP v1: remotely invoking searchAmazon
      SOAP v1: remotely invoking searchGoogle
      
      你可以看到,soap-v1.jar被首先加在classpath中,因此實際上也只有他會被使用。上面的例子等價于:
      $ java -classpath
      metasearch.jar:amazon.jar:google.jar:soap-v1.jar:soap-v2.jar
      # WRONG!
      
      編者注:上面的命令行應(yīng)該在同一行鍵入;只是由于網(wǎng)頁布局的限制看起來好像是多行。
      
      有趣的是如果Yahoo也發(fā)布了一個網(wǎng)絡(luò)服務(wù)API,而他看起來并沒有依賴于現(xiàn)有的SOAP/XML-RPC類庫。在較小的項目中,組件依賴沖突常被用來作為在你只要手工包裝方案或者只需要一兩個類時而不使用讓你不使用全量組件(如集合類庫)的原因之一。手工包裝方案有他的用處,但使用已有的組件是更普遍的方式。而且復(fù)制其他組件的類到你的代碼庫永遠(yuǎn)不是一個好主意。實際上你已經(jīng)與組件的開發(fā)產(chǎn)生分岐而且沒有機會在有問題修復(fù)或安全升級時合并他。
      
      許多大的項目,如主要的商業(yè)組件,已經(jīng)采用將他們使用的整個組件構(gòu)建到他們的JAR內(nèi)部。為了這么做,他們改變了包名使其唯一(如com/acme/foobar/org/freeware/utility),而且直接在他們的JAR中包含類。這樣做的好處是可以防止在這些組件中多個版本的沖突,但這也是有代價的。這么做對開發(fā)人員來說完全隱藏了對第三方的依賴。但如果這種方式大規(guī)模的應(yīng)用,將會導(dǎo)致效率的降低(包括JAR文件的大小和加載多個JAR版本到進程中的效率降低)。這種方式的問題在于如果兩個組件依賴于同一個版本的第三方組件時,就沒有協(xié)調(diào)機制來確定共享的組件只被加載一次。這個問題我們會在下一節(jié)進行研究。除了效率的降低外,很可能你這種綁定第三方軟件的方式會與那些軟件的許可協(xié)議沖突。
      
      另一種解決這個問題的方式是每一個組件的開發(fā)人員顯式的在他的包名中編碼一個版本號。Sun的javac代碼就采用這個方式—一個com.sun.tools.javac.Main類會簡單地轉(zhuǎn)發(fā)給com.sun.tools.javac.v8.Maino。每次一個新的Java版本發(fā)布,這個代碼的包名就改變一次。這就允許一個組件的多個發(fā)布版本可以共存在同一個類加載器中并且這使得版本的選擇是顯式的。但這也不是一個非常好的解決方案,因為或者客戶需要準(zhǔn)確知道他們計劃使用的版本而且必須改變他們的代碼來轉(zhuǎn)換到新的版本,或者他們必須依賴于一個包裝類來轉(zhuǎn)發(fā)方案調(diào)用給最新的版本(在這種情況下,這些包裝類就會承受我們上面提到的相同問題)。
      
      加載多個發(fā)布版本
      
      這里我們遇到的問題在大多數(shù)項目中也存在,所有的類都會被加載到一個全局命名空間。如果每一個組件有自己的命名空間而且他會加載所有他依賴的組件到這個命名空間而不影響進程的其他部分,那又會怎么樣呢?實際上我們可以在Java中這么做!類名不需要是唯一的,只要類名和其所對應(yīng)的ClassLoader的組合是唯一的就可以了。這意味著ClassLoader類似于命名空間,而如果我們可以加載每一個組件在自己的ClassLoader中,他就可以控制如何滿足依賴。他可以代理類定位給其他的包含他的依賴組件所需要的特定版本的ClassLoader。如圖1。
      
     
      Figure 1. Decentralized class loaders

      
      然而這個架構(gòu)并不比綁定每一個依賴的JAR在自己的JAR中好多少。我們需要的是一個可以確保每一個組件版本僅被一個類加載器加載的中央集權(quán)。圖2中的架構(gòu)可以確定每一個組件版本僅被加載一次。
      
     
      Figure 2. Class loaders with mediator

      
      為了實現(xiàn)這種方式,我們需要創(chuàng)建兩個不同類型的類加載器。每一個ComponentClassLoader需要擴展Java的URLClassLoader來提供需要的邏輯來從一個JAR中獲取.class文件。當(dāng)然他也會執(zhí)行兩個其他的任務(wù)。在創(chuàng)建的時候,他會獲取JAR清單文件并定位一個新屬性Restricted-Class-Path。不像Sun提供的Class-Path屬性,這個屬性暗示特定的JAR應(yīng)該只對這個組件有效。
      public class ComponentClassLoader extends URLClassLoader {
      // ... public ComponentClassLoader (MasterClassLoader master, File file)
      {
      // ...  JarFile jar = new JarFile(file);
      Manifest man = jar.getManifest();
      Attributes attr = man.getMainAttributes();
      List l = new ArrayList();
      String str = attr.getValue("Restricted-Class-Path");
      if (str != null) {
      StringTokenizer tok = new StringTokenizer(str);
      while (tok.hasMoreTokens()) {
      l.add(new File(file.getParentFile(),
      tok.nextToken());
      }
      }
      this.dependencies = l;
      } public Class loadClass (String name, boolean resolve)
      throws ClassNotFoundException {
      try {
      // Try to load the class from our JAR.
      return loadClassForComponent(name, resolve);
      } catch (ClassNotFoundException ex) {}
      // Couldn't find it -- let the master look for it
      // in another components.
      return master.loadClassForComponent(name,
      resolve, dependencies);
      }
      public Class loadClassForComponent (String name,
      boolean resolve)
      throws ClassNotFoundException
      {
      C

    posted on 2006-04-25 09:44 迷途書童 閱讀(392) 評論(0)  編輯  收藏 所屬分類: 深入jvm


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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 国产高清视频免费在线观看 | 精品成人免费自拍视频| 最近中文字幕无免费视频| 亚洲AV人人澡人人爽人人夜夜| 一级做a爱过程免费视| 亚洲精品国产精品国自产观看 | 在线视频网址免费播放| 亚洲一级片免费看| 久久国产精品免费一区| 亚洲精品一品区二品区三品区| 波霸在线精品视频免费观看| 亚洲日产无码中文字幕| 无码午夜成人1000部免费视频| 亚洲男人第一av网站| 美丽的姑娘免费观看在线播放| 亚洲黄色免费在线观看| 成人福利免费视频| 亚洲精品又粗又大又爽A片| 在线观看亚洲免费视频| 国产黄片不卡免费| 亚洲天堂视频在线观看| 在线观看免费人成视频色9| 亚洲AV无码一区二区大桥未久| 亚洲国产精品尤物YW在线观看| 天堂在线免费观看| 久久久久久亚洲Av无码精品专口 | 免费一看一级毛片人| 一级毛片aa高清免费观看| 久久噜噜噜久久亚洲va久| 国产92成人精品视频免费| 午夜亚洲国产理论片二级港台二级 | 亚洲精品国产精品| 亚洲日韩在线观看| 一级毛片不卡片免费观看| 亚洲人av高清无码| 中文字幕精品亚洲无线码一区应用| 91精品免费高清在线| 亚洲av无码有乱码在线观看| 亚洲一区二区精品视频| 99国产精品视频免费观看| 国内成人精品亚洲日本语音|