国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

用 One-JAR 簡化應(yīng)用程序交付

2019-11-18 13:08:59
字體:
供稿:網(wǎng)友

  假如您曾經(jīng)試圖把 java 應(yīng)用程序交付為單一的 Java 檔案文件(JAR 文件),那么您很有可能碰到過這樣的需求:在構(gòu)建最終檔案文件之前,要展開支持 JAR 文件(supporting JAR file)。這不但是一個(gè)開發(fā)的難點(diǎn),還有可能讓您違反許可協(xié)議。在本文中,Tuffs 向您介紹了 One-JAR 這個(gè)工具,它使用定制的類裝入器,動(dòng)態(tài)地從可執(zhí)行 JAR 文件內(nèi)部的 JAR 文件中裝入類。
  有人曾經(jīng)說過,歷史總是在不斷地重復(fù)自身,首先是悲劇,然后是鬧劇。 最近,我第一次對(duì)此有了親身體會(huì)。我不得不向客戶交付一個(gè)可以運(yùn)行的 Java 應(yīng)用程序,但是我已經(jīng)交付了許多次,它總是布滿了復(fù)雜性。在搜集應(yīng)用程序的所有 JAR 文件、為 DOS 和 Unix(以及 Cygwin)編寫啟動(dòng)腳本、確保客戶端環(huán)境變量都指向正確位置的時(shí)候,總是有許多輕易出錯(cuò)的地方。假如每件事都能做好,那么應(yīng)用程序能夠按它預(yù)期的方式運(yùn)行。但是在出現(xiàn)麻煩時(shí)(而這又是常見的情況),結(jié)果就是大量時(shí)間耗費(fèi)在客戶端支持上。
  
  最近與一個(gè)被大量 ClassNotFound 異常弄得暈頭轉(zhuǎn)向的客戶交談之后,我決定自己再也不能忍受下去了。所以,我轉(zhuǎn)而尋找一個(gè)方法,可以把我的應(yīng)用程序打包到單一 JAR 文件中,給我的客戶提供一個(gè)簡單的機(jī)制(比如 java -jar)來運(yùn)行程序。
  
  努力的結(jié)果就是 One-JAR,一個(gè)非常簡單的軟件打包解決方案,它利用 Java 的定制類裝入器,動(dòng)態(tài)地從單一檔案文件中裝入應(yīng)用程序所有的類,同時(shí)保留支持 JAR 文件的結(jié)構(gòu)。在本文中,我將介紹我開發(fā) One-JAR 的過程,然后告訴您如何利用它在一個(gè)自包含的文件中交付您自己的可以運(yùn)行的應(yīng)用程序。
  
  One-JAR 概述
  在介紹 One-JAR 的細(xì)節(jié)之前,請讓我首先討論一下我構(gòu)建它的目的。我確定一個(gè) One-JAR 檔案文件應(yīng)該是:
  
  可以用 java -jar 機(jī)制執(zhí)行。
  
  能夠包含應(yīng)用程序需要的 所有 文件 —— 也就是說,包括原始形式(未展開)的類和資源。
  
  擁有簡單的內(nèi)部結(jié)構(gòu),僅僅用 jar 工具就可以被裝配起來。
  
  對(duì)原來的應(yīng)用程序不可見 —— 也就是說,無需修改原來的應(yīng)用程序,就可以把它打包在 One-JAR 檔案文件內(nèi)部。
  
  問題和解決方案
  在開發(fā) One-JAR 的過程中,我解決的最大問題,就是如何裝入包含在另外一個(gè) JAR 文件中的 JAR 文件。 Java 類裝入器 sun.misc.Launcher$AppClassLoader(在 java -jar 開始的時(shí)候出現(xiàn))只知道如何做兩件事:
  
  裝入在 JAR 文件的根出現(xiàn)的類和資源。
  
  裝入 META-INF/MANIFEST.MF 中的 Class-Path 屬性指向的代碼基中的類和資源。
  而且,它還故意忽略針對(duì) CLASSPATH 的全部環(huán)境變量設(shè)置,還忽略您提供的命令行參數(shù) -cp 。所以它不知道如何從一個(gè)包含在其他 JAR 文件中的 JAR 文件裝入類或資源。
  
  顯然,我需要克服這個(gè)問題,才能實(shí)現(xiàn) One-JAR 的目標(biāo)。
  
  解決方案 1:展開支持 JAR 文件
  我為了創(chuàng)建單一可執(zhí)行 JAR 文件所做的第一個(gè)嘗試,顯然就是在可交付的 JAR 文件內(nèi)展開支持 JAR 文件,我們把可交付的文件稱為 main.jar。假設(shè)有一個(gè)應(yīng)用程序的類叫做 com.main.Main,而且它依靠兩個(gè)類 —— com.a.A (在 a.jar 中) 和 com.b.B(在 b.jar 中),那么 One-JAR 文件看起來應(yīng)該像這樣:
  
  main.jar
   com/main/Main.class
   com/a/A.class
   com/b/B.class
  
  這樣,最初來源于 a.jar 文件的 A.class 丟失了,B.class 也是如此。雖然這看起來只是個(gè)小問題,但卻會(huì)真正帶來問題,我很快就會(huì)解釋為什么。
  
  One-JAR 和 FJEP
  最近發(fā)布的一個(gè)叫做 FJEP (FatJar Eclipse Plugin) 的工具支持在 Eclipse 內(nèi)部直接構(gòu)建扁平 JAR 文件。 One-JAR 已經(jīng)與 FatJar 集成在一起,以支持在不展開 JAR 文件的情況下嵌入 JAR 文件。請參閱 參考資料 了解有關(guān)具體內(nèi)容。
  
  把 JAR 文件展開到文件系統(tǒng)以創(chuàng)建一個(gè)扁平結(jié)構(gòu),這可能非常耗時(shí)。還需要使用 Ant 這樣的構(gòu)建工具來展開和重新歸檔支持類。
  
  除了這個(gè)小麻煩之外,我很快又碰到了兩個(gè)與展開支持 JAR 文件有關(guān)的嚴(yán)重問題:
  
  假如 a.jar 和 b.jar 包含的資源的路徑名相同 (比如說,都是 log4j.PRoperties ),那么您該選哪個(gè)?
  
  假如 b.jar 的許可明確要求您在重新發(fā)布它的時(shí)候不能修改它,那您怎么辦?您無法在不破壞許可條款的前提下像這樣展開它。
  我覺得這些限制為另外一種方法提供了線索。
  
  解決方案 2: MANIFEST Class-Path
  我決定研究 java -jar 裝入器中的另外一種機(jī)制:裝入的類是在檔案文件中一個(gè)叫做 META-INF/MANIFEST.MF 的非凡文件中指定的。通過指定稱為 Class-Path 的屬性,我希望能夠向啟動(dòng)時(shí)的類裝入器添加其他檔案文件。下面就是這樣的一個(gè) One-JAR 文件看起來的樣子:
  
  main.jar
   META-INF/MANIFEST.MF
   + Class-Path: lib/a.jar lib/b.jar
   com/main/Main.class
   lib/a.jar
   lib/b.jar
  
  說明與線索
  URLClassloader 是 sun.misc.Launcher$AppClassLoader 的基類,它支持一個(gè)相當(dāng)神秘的 URL 語法,讓您能夠引用 JAR 文件內(nèi)部的資源。這個(gè)語法用起來像這樣: jar:file:/fullpath/main.jar!/a.resource。
  
  從理論上講,要獲得一個(gè)在 JAR 文件 內(nèi)部 的 JAR 文件中的項(xiàng),您必須使用像 jar:file:/fullpath/main.jar!/lib/a.jar!/a.resource 這樣的方式,但是很不幸,這么做沒有用。JAR 文件協(xié)議處理器在找 JAR 文件時(shí),只熟悉最后一個(gè) “!/” 分隔符。
  
  但是,這個(gè)語法確實(shí)為我最終的 One-JAR 解決方案提供了線索……
  
  這能工作么? 當(dāng)我把 main.jar 移動(dòng)到另外一個(gè)地方,并試著運(yùn)行它時(shí),似乎是可以了。為了裝配 main.jar ,我創(chuàng)建了一個(gè)名為 lib 的子目錄,并把 a.jar 和 b.jar 放在里面。不幸的是,應(yīng)用程序的類裝入器只從文件系統(tǒng)提取支持 JAR 文件,而不能從嵌入的 JAR 文件中裝入類。
  
  為了克服這一問題,我試著用神秘的 jar:!/ 語法的幾種變體來使用 Class-Path(請參閱 “說明和線索”),但是沒有一次成功。我能 做的,就只有分別交付 a.jar 和 b.jar ,并把它們與 main.jar 一起放在文件系統(tǒng)中了;但是這正是我想避免的那類事情。
  
  進(jìn)入 JarClassLoader
  此時(shí),我感到備受挫折。我如何才能讓應(yīng)用程序從它自己的 JAR 文件中的 lib 目錄裝入它自己的類呢?我決定應(yīng)當(dāng)創(chuàng)建定制類裝入器來承擔(dān)這個(gè)重任。編寫定制類裝入器不是一件輕易的事情。但是實(shí)際上這個(gè)工作并沒有那么復(fù)雜,類裝入器對(duì)它所控制的應(yīng)用程序有非常深刻的影響,所以在發(fā)生故障的時(shí)候,很難診斷和解釋故障。雖然對(duì)于類裝入的完整處理超出了本文的范圍(請參閱 參考資料),我還是要介紹一些基本概念,好保證您能從后面的討論中得到最大收獲。
  
  裝入類
  當(dāng) JVM 碰到一個(gè)對(duì)象的類未知的時(shí)候,就會(huì)調(diào)用類裝入器。類裝入器的工作是找到類的字節(jié)碼(基于類的名稱),然后把這些字節(jié)傳遞給 JVM,JVM 再把這些字節(jié)碼鏈接到系統(tǒng)的其余部分,使得正在運(yùn)行的代碼可以使用新裝入的類。JDK 中要害的類是 java.lang.Classloader 以及 loadClass 方法,摘要如下:
  
  public abstract class ClassLoader {
  ...
  protected synchronized Class loadClass(String name, boolean resolve)
  throws ClassNotFoundException {...}
  }
  
  ClassLoader 類的主要入口點(diǎn)是 loadClass() 方法。您會(huì)注重到, ClassLoader 是一個(gè)抽象類,但是它沒有聲明任何抽象方法,這樣,關(guān)于 loadClass() 方法是不是要關(guān)注的方法,一點(diǎn)線索也沒留下。實(shí)際上,它不是 要關(guān)注的主方法:回到過去的好時(shí)光,看看 JDK 1.1 的類裝入器,可以看到 loadClass() 是您可以有效擴(kuò)展類裝入器的惟一地方,但是從 JDK 1.2 起,最好讓類裝入器單獨(dú)做它所做的工作,即以下工作:
  
  檢查類是否已經(jīng)裝入。
  檢查上級(jí)類裝入器能否裝入類。
  調(diào)用 findClass(String name) 方法,讓派生的類裝入器裝入類。
  ClassLoader.findClass() 的實(shí)現(xiàn)是拋出一個(gè)新的 ClassNotFoundException 異常,并且是我們實(shí)現(xiàn)定制類裝入器時(shí)要考慮的第一個(gè)方法。
  
  JAR 文件何時(shí)不是 JAR 文件?
  為了能夠裝入在 JAR 文件內(nèi)部 的 JAR 文件中的類(這是要害問題,您可以回想起來),我首先必須能夠打開并讀取頂層的 JAR 文件(上面的 main.jar 文件)。現(xiàn)在,因?yàn)槲沂褂玫氖?java -jar 機(jī)制,所以, java.class.path 系統(tǒng)屬性中的第一個(gè)(也是惟一一個(gè))元素是 One-JAR 文件的完整路徑名!用下面的代碼您可以得到它:
  
  jarName = System.getProperty("java.class.path");
  
  我接下來的一步是遍歷應(yīng)用程序的所有 JAR 文件項(xiàng),并把它們裝入內(nèi)存,如清單 1 所示:
  
  清單 1. 遍歷查找嵌入的 JAR 文件
  
  JarFile jarFile = new JarFile(jarName);
  Enumeration enum = jarFile.entries();
  while (enum.hasMoreElements()) {
  JarEntry entry = (JarEntry)enum.nextElement();
  if (entry.isDirectory()) continue;
  String jar = entry.getName();
  if (jar.startsWith(LIB_PREFIX) jar.startsWith(MAIN_PREFIX)) {
  // Load it!
  InputStream is = jarFile.getInputStream(entry);
  if (is == null)
  throw new IOException("Unable to load resource /" + jar + " using " + this);
  loadByteCode

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 宁陕县| 龙陵县| 马尔康县| 二连浩特市| 神农架林区| 龙游县| 临澧县| 南部县| 阿合奇县| 富蕴县| 云南省| 沙洋县| 锡林郭勒盟| 长沙市| 铁力市| 泗洪县| 南和县| 巴林左旗| 天气| 昔阳县| 辽阳市| 方城县| 万源市| 蒙城县| 巫溪县| 洛扎县| 荣成市| 浠水县| 莒南县| 门源| 应城市| 大洼县| 湘西| 鄂尔多斯市| 元氏县| 昂仁县| 平原县| 新田县| 西和县| 青冈县| 两当县|