JAR?文件是什么?
JAR?文件格式以流行的?ZIP?文件格式為基礎,用于將許多個文件聚集為一個文件。與?ZIP?文件不同的是,JAR?文件不僅用于壓縮和發布,而且還用于部署和封裝庫、組件和插件程序,并可被像編譯器和?JVM?這樣的工具直接使用。在?JAR?中包含特殊的文件,如?manifests?和部署描述符,用來指示工具如何處理特定的?JAR。
一個?JAR?文件可以用于:
□?用于發布和使用類庫?
□?作為應用程序和擴展的構建單元?
□?作為組件、applet?或者插件程序的部署單位?
□?用于打包與組件相關聯的輔助資源?
JAR?文件格式提供了許多優勢和功能,其中很多是傳統的壓縮格式如?ZIP?或者?TAR?所沒有提供的。它們包括:
☆?安全性。?可以對?JAR?文件內容加上數字化簽名。這樣,能夠識別簽名的工具就可以有選擇地為您授予軟件安全特權,這是其他文件做不到的,它還可以檢測代碼是否被篡改過。
☆?減少下載時間。?如果一個?applet?捆綁到一個?JAR?文件中,那么瀏覽器就可以在一個?HTTP?事務中下載這個?applet?的類文件和相關的資源,而不是對每一個文件打開一個新連接。
☆?壓縮。JAR?格式允許您壓縮文件以提高存儲效率。
☆?傳輸平臺擴展。?Java?擴展框架(Java?Extensions?Framework)提供了向?Java?核心平臺添加功能的方法,這些擴展是用?JAR?文件打包的(Java?3D?和?JavaMail?就是由?Sun?開發的擴展例子)。
☆?包密封。?存儲在?JAR?文件中的包可以選擇進行密封,以增強版本一致性和安全性。密封一個包意味著包中的所有類都必須在同一?JAR?文件中找到。
☆?包版本控制。?一個?JAR?文件可以包含有關它所包含的文件的數據,如廠商和版本信息。
☆?可移植性。?處理?JAR?文件的機制是?Java?平臺核心?API?的標準部分。?
壓縮的和未壓縮的?JAR
jar?工具在默認情況下壓縮文件。未壓縮的?JAR?文件一般可以比壓縮過的?JAR?文件更快地裝載,因為在裝載過程中要解壓縮文件,但是未壓縮的文件在網絡上的下載時間可能更長。
META-INF?目錄
大多數?JAR?文件包含一個?META-INF?目錄,它用于存儲包和擴展的配置數據,如安全性和版本信息。Java?2?平臺識別并解釋?META-INF?目錄中的下述文件和目錄,以便配置應用程序、擴展和類裝載器:
☆?MANIFEST.MF。?這個?manifest?文件定義了與擴展和包相關的數據。
☆?INDEX.LIST。?這個文件由?jar?工具的新選項?-i?生成,它包含在應用程序或者擴展中定義的包的位置信息。它是?JarIndex?實現的一部分,并由類裝載器用于加速類裝載過程。
☆?xxx.SF。?這是?JAR?文件的簽名文件。占位符?xxx?標識了簽名者。
☆?xxx.DSA。?與簽名文件相關聯的簽名程序塊文件,它存儲了用于簽名?JAR?文件的公共簽名。?
jar?工具
為了用?JAR?文件執行基本的任務,要使用作為Java?Development?Kit?的一部分提供的?Java?Archive?Tool?(jar?工具)。用?jar?命令調用?jar?工具。表?1?顯示了一些常見的應用:
表?1.?常見的?jar?工具用法?功能?命令?
用一個單獨的文件創建一個?JAR?文件?jar?cf?jar-file?input-file...?
用一個目錄創建一個?JAR?文件?jar?cf?jar-file?dir-name?
創建一個未壓縮的?JAR?文件?jar?cf0?jar-file?dir-name?
更新一個?JAR?文件?jar?uf?jar-file?input-file...?
查看一個?JAR?文件的內容?jar?tf?jar-file?
提取一個?JAR?文件的內容?jar?xf?jar-file?
從一個?JAR?文件中提取特定的文件?jar?xf?jar-file?archived-file...?
運行一個打包為可執行?JAR?文件的應用程序?java?-jar?app.jar?
可執行的?JAR
一個可執行的?jar?文件是一個自包含的?Java?應用程序,它存儲在特別配置的JAR?文件中,可以由?JVM?直接執行它而無需事先提取文件或者設置類路徑。要運行存儲在非可執行的?JAR?中的應用程序,必須將它加入到您的類路徑中,并用名字調用應用程序的主類。但是使用可執行的?JAR?文件,我們可以不用提取它或者知道主要入口點就可以運行一個應用程序。可執行?JAR?有助于方便發布和執行?Java?應用程序。
創建可執行?JAR
創建一個可執行?JAR?很容易。首先將所有應用程序代碼放到一個目錄中。假設應用程序中的主類是?com.mycompany.myapp.Sample。您要創建一個包含應用程序代碼的?JAR?文件并標識出主類。為此,在某個位置(不是在應用程序目錄中)創建一個名為?manifest?的文件,并在其中加入以下一行:
Main-Class:?com.mycompany.myapp.Sample
然后,像這樣創建?JAR?文件:
jar?cmf?manifest?ExecutableJar.jar?application-dir
所要做的就是這些了?--?現在可以用?java?-jar?執行這個?JAR?文件?ExecutableJar.jar。
一個可執行的?JAR?必須通過?menifest?文件的頭引用它所需要的所有其他從屬?JAR。如果使用了?-jar?選項,那么環境變量?CLASSPATH?和在命令行中指定的所有類路徑都被?JVM?所忽略。
啟動可執行?JAR
既然我們已經將自己的應用程序打包到了一個名為?ExecutableJar.jar?的可執行?JAR?中了,那么我們就可以用下面的命令直接從文件啟動這個應用程序:
java?-jar?ExecutableJar.jar
包密封
密封?JAR?文件中的一個包意味著在這個包中定義的所有類都必須在同一個?JAR?文件中找到。這使包的作者可以增強打包類之間的版本一致性。密封還提供了防止代碼篡改的手段。
要密封包,需要在?JAR?的?manifest?文件中為包添加一個?Name?頭,然后加上值為“true”的?Sealed?頭。與可執行的?JAR?一樣,可以在創建?JAR?時,通過指定一個具有適當頭元素的?manifest?文件密封一個?JAR,如下所示:
Name:?com/samplePackage/
Sealed:?true
Name?頭標識出包的相對路徑名。它以一個“/”結束以與文件名區別。在?Name?頭后面第一個空行之前的所有頭都作用于在?Name?頭中指定的文件或者包。在上述例子中,因為?Sealed?頭出現在?Name?頭后并且中間沒有空行,所以?Sealed?頭將被解釋為只應用到包?com/samplePackage?上。
如果試圖從密封包所在的?JAR?文件以外的其他地方裝載密封包中的一個類,那么?JVM?將拋出一個?SecurityException。
擴展打包
擴展為?Java?平臺增加了功能,在?JAR?文件格式中已經加入了擴展機制。擴展機制使得?JAR?文件可以通過?manifest?文件中的?Class-Path?頭指定所需要的其他?JAR?文件。
假設?extension1.jar?和?extension2.jar?是同一個目錄中的兩個?JAR?文件,extension1.jar?的?manifest?文件包含以下頭:
Class-Path:?extension2.jar
這個頭表明?extension2.jar?中的類是?extension1.jar?中的類的擴展類。extension1.jar?中的類可以調用?extension2.jar?中的類,并且不要求?extension2.jar?處在類路徑中。
在裝載使用擴展機制的?JAR?時,JVM?會高效而自動地將在Class-Path?頭中引用的?JAR?添加到類路徑中。不過,擴展?JAR?路徑被解釋為相對路徑,所以一般來說,擴展?JAR?必須存儲在引用它的?JAR?所在的同一目錄中。
例如,假設類?ExtensionClient?引用了類?ExtensionDemo,它捆綁在一個名為?ExtensionClient.jar?的?JAR?文件中,而類?ExtensionDemo?則捆綁在?ExtensionDemo.jar?中。為了使?ExtensionDemo.jar?可以成為擴展,必須將?ExtensionDemo.jar?列在?ExtensionClient.jar?的?manifest?的?Class-Path?頭中,如下所示:
Manifest-Version:?1.0
Class-Path:?ExtensionDemo.jar
在這個?manifest?中?Class-Path?頭的值是沒有指定路徑的?ExtensionDemo.jar,表明?ExtensionDemo.jar?與?ExtensionClient?JAR?文件處在同一目錄中。
JAR?文件中的安全性
JAR?文件可以用?jarsigner?工具或者直接通過?java.security?API?簽名。一個簽名的?JAR?文件與原來的?JAR?文件完全相同,只是更新了它的?manifest,并在?META-INF?目錄中增加了兩個文件,一個簽名文件和一個簽名塊文件。
JAR?文件是用一個存儲在?Keystore?數據庫中的證書簽名的。存儲在?keystore?中的證書有密碼保護,必須向?jarsigner?工具提供這個密碼才能對?JAR?文件簽名。
圖?1.?Keystore?數據庫
JAR?的每一位簽名者都由在?JAR?文件的?META-INF?目錄中的一個具有?.SF?擴展名的簽名文件表示。這個文件的格式類似于?manifest?文件?--?一組?RFC-822?頭。如下所示,它的組成包括一個主要部分,它包括了由簽名者提供的信息、但是不特別針對任何特定的?JAR?文件項,還有一系列的單獨的項,這些項也必須包含在?menifest?文件中。在驗證一個簽名的?JAR?時,將簽名文件的摘要值與對?JAR?文件中的相應項計算的摘要值進行比較。
清單?1.?簽名?JAR?中的?Manifest?和?signature?文件
Contents?of?signature?file?META-INF/MANIFEST.MF
Manifest-Version:?1.0
Created-By:?1.3.0?(Sun?Microsystems?Inc.)
Name:?Sample.java
SHA1-Digest:?3+DdYW8INICtyG8ZarHlFxX0W6g=
Name:?Sample.class
SHA1-Digest:?YJ5yQHBZBJ3SsTNcHJFqUkfWEmI=
Contents?of?signature?file?META-INF/JAMES.SF
Signature-Version:?1.0
SHA1-Digest-Manifest:?HBstZOJBuuTJ6QMIdB90T8sjaOM=
Created-By:?1.3.0?(Sun?Microsystems?Inc.)
Name:?Sample.java
SHA1-Digest:?qipMDrkurQcKwnyIlI3Jtrnia8Q=
Name:?Sample.class
SHA1-Digest:?pT2DYby8QXPcCzv2NwpLxd8p4G4=
數字簽名
一個數字簽名是.SF?簽名文件的已簽名版本。數字簽名文件是二進制文件,并且與?.SF?文件有相同的文件名,但是擴展名不同。根據數字簽名的類型?--?RSA、DSA?或者?PGP?--?以及用于簽名?JAR?的證書類型而有不同的擴展名。
Keystore
要簽名一個?JAR?文件,必須首先有一個私鑰。私鑰及其相關的公鑰證書存儲在名為?keystores?的、有密碼保護的數據庫中。JDK?包含創建和修改?keystores?的工具。keystore?中的每一個密鑰都可以用一個別名標識,它通常是擁有這個密鑰的簽名者的名字。
所有?keystore?項(密鑰和信任的證書項)都是用唯一別名訪問的。別名是在用?keytool?-genkey?命令生成密鑰對(公鑰和私鑰)并在?keystore?中添加項時指定的。之后的?keytool?命令必須使用同樣的別名引用這一項。
例如,要用別名“james”生成一個新的公鑰/私鑰對并將公鑰包裝到自簽名的證書中,要使用下述命令:
keytool?-genkey?-alias?james?-keypass?jamespass?
????????-validity?80?-keystore?jamesKeyStore?
????????-storepass?jamesKeyStorePass
這個命令序列指定了一個初始密碼“jamespass”,后續的命令在訪問?keystore?“jamesKeyStore”中與別名“james”相關聯的私鑰時,就需要這個密碼。如果?keystore“jamesKeyStore”不存在,則?keytool?會自動創建它。
jarsigner?工具
jarsigner?工具使用?keystore?生成或者驗證?JAR?文件的數字簽名。
假設像上述例子那樣創建了?keystore?“jamesKeyStore”,并且它包含一個別名為“james”的密鑰,可以用下面的命令簽名一個?JAR?文件:
jarsigner?-keystore?jamesKeyStore?-storepass?jamesKeyStorePass?
??????????-keypass?jamespass?-signedjar?SSample.jar?Sample.jar?james
這個命令用密碼“jamesKeyStorePass”從名為“jamesKeyStore”的?keystore?中提出別名為“james”、密碼為“jamespass”的密鑰,并對?Sample.jar?文件簽名、創建一個簽名的?JAR?--?SSample.jar。
jarsigner?工具還可以驗證一個簽名的?JAR?文件,這種操作比簽名?JAR?文件要簡單得多,只需執行以下命令:
jarsigner?-verify?SSample.jar
如果簽名的?JAR?文件沒有被篡改過,那么?jarsigner?工具就會告訴您?JAR?通過驗證了。否則,它會拋出一個?SecurityException,?表明哪些文件沒有通過驗證。
還可以用?java.util.jar?和?java.security?API?以編程方式簽名?JAR(有關細節參閱參考資料)。也可以使用像?Netscape?Object?Signing?Tool?這樣的工具。
JAR?索引
如果一個應用程序或者?applet?捆綁到多個?JAR?文件中,那么類裝載器就使用一個簡單的線性搜索算法搜索類路徑中的每一個元素,這使類裝載器可能要下載并打開許多個?JAR?文件,直到找到所要的類或者資源。如果類裝載器試圖尋找一個不存在的資源,那么在應用程序或者?applet?中的所有?JAR?文件都會下載。對于大型的網絡應用程序和?applet,這會導致啟動緩慢、響應遲緩并浪費帶寬。
從?JDK?1.3?以后,JAR?文件格式開始支持索引以優化網絡應用程序中類的搜索過程,特別是?applet。JarIndex?機制收集在?applet?或者應用程序中定義的所有?JAR?文件的內容,并將這些信息存儲到第一個?JAR?文件中的索引文件中。下載了第一個?JAR?文件后,applet?類裝載器將使用收集的內容信息高效地裝載?JAR?文件。這個目錄信息存儲在根?JAR?文件的?META-INF?目錄中的一個名為?INDEX.LIST?的簡單文本文件中。
創建一個?JarIndex
可以通過在?jar?命令中指定?-i?選項創建一個?JarIndex。假設我們的目錄結構如下圖所示:
圖?2.?JarIndex
您將使用下述命令為?JarIndex_Main.jar、JarIndex_test.jar?和?JarIndex_test1.jar?創建一個索引文件:
jar?-i?JarIndex_Main.jar?JarIndex_test.jar?SampleDir/JarIndex_test1.jar?
INDEX.LIST?文件的格式很簡單,包含每個已索引的?JAR?文件中包含的包或者類的名字,如清單?2?所示:
清單?2.?JarIndex?INDEX.LIST?文件示例
JarIndex-Version:?1.0
JarIndex_Main.jar
sp
JarIndex_test.jar
Sample
SampleDir/JarIndex_test1.jar
org
org/apache
org/apache/xerces
org/apache/xerces/framework
org/apache/xerces/framework/xml4j
結束語?
JAR?格式遠遠超出了一種壓縮格式,它有許多可以改進效率、安全性和組織?Java?應用程序的功能。因為這些功能已經建立在核心平臺?--?包括編譯器和類裝載器?--?中了,所以開發人員可以利用?JAR?文件格式的能力簡化和改進開發和部署過程。