打開Java的JAR文件咱們常常能夠看到文件中包含着一個META-INF目錄, 這個目錄下會有一些文件,其中必有一個MANIFEST.MF,這個文件描述了該Jar文件的不少信息。我的理解,MANIFEST.MF文件是jar文件運行依賴的入口,maven打jar包時,MANIFEST.MF文件配置不正確,會致使項目部署失敗。html
若是咱們把MANIFEST中的配置信息進行分類,能夠概括出下面幾個大類:java
一. 通常屬性程序員
1. Manifest-Version
用來定義manifest文件的版本,例如:Manifest-Version: 1.0
2. Created-By
聲明該文件的生成者,通常該屬性是由jar命令行工具生成的,例如:Created-By: Apache Ant 1.5.1
3. Signature-Version
定義jar文件的簽名版本
4. Class-Path
應用程序或者類裝載器使用該值來構建內部的類搜索路徑算法
二. 應用程序相關屬性數據庫
1. Main-Class
定義jar文件的入口類,該類必須是一個可執行的類,一旦定義了該屬性便可經過 java -jar x.jar來運行該jar文件。
三. 小程序(Applet)相關屬性apache
1. Extendsion-List
該屬性指定了小程序須要的擴展信息列表,列表中的每一個名字對應如下的屬性
2. <extension>-Extension-Name
3. <extension>-Specification-Version
4. <extension>-Implementation-Version
5. <extension>-Implementation-Vendor-Id
5. <extension>-Implementation-URL編程
四. 擴展標識屬性小程序
1. Extension-Name
該屬性定義了jar文件的標識,例如Extension-Name: Struts Framework
五. 包擴展屬性
1. Implementation-Title 定義了擴展實現的標題
2. Implementation-Version 定義擴展實現的版本
3. Implementation-Vendor 定義擴展實現的組織
4. Implementation-Vendor-Id 定義擴展實現的組織的標識
5. Implementation-URL : 定義該擴展包的下載地址(URL)
6. Specification-Title 定義擴展規範的標題
7. Specification-Version 定義擴展規範的版本
8. Specification-Vendor 聲明瞭維護該規範的組織
9. Sealed 定義jar文件是否封存,值能夠是true或者false (這點我還不是很理解)瀏覽器
六. 簽名相關屬性安全
簽名方面的屬性咱們能夠來參照JavaMail所提供的mail.jar中的一段
Name: javax/mail/Address.class
Digest-Algorithms: SHA MD5
SHA-Digest: AjR7RqnN//cdYGouxbd06mSVfI4=
MD5-Digest: ZnTIQ2aQAtSNIOWXI1pQpw==
這段內容定義類簽名的類名、計算摘要的算法名以及對應的摘要內容(使用BASE方法進行編碼)
七.自定義屬性
除了前面提到的一些屬性外,你也能夠在MANIFEST.MF中增長本身的屬性以及響應的值,例如J2ME程序jar包中就可能包含着以下信息
MicroEdition-Configuration: CLDC-1.0
MIDlet-Name: J2ME_MOBBER Midlet Suite
MIDlet-Info-URL: http://www.javayou.com
MIDlet-Icon: /icon.png
MIDlet-Vendor: Midlet Suite Vendor
MIDlet-1: mobber,/icon.png,mobber
MIDlet-Version: 1.0.0
MicroEdition-Profile: MIDP-1.0
MIDlet-Description: Communicator
關 鍵在於咱們怎麼來讀取這些信息呢?其實很簡單,JDK給咱們提供了用於處理這些信息的API,詳細的信息請見java.util.jar包中,咱們能夠通 過給JarFile傳遞一個jar文件的路徑,而後調用JarFile的getManifest方法來獲取Manifest信息。
更詳細關於JAR文件的規範請見
http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html
中文說明
http://www-900.ibm.com/developerWorks/cn/java/j-jar/
附:
大多數 Java 程序員都熟悉對 JAR 文件的基本操做。可是隻有少數程序員瞭解 JAR 文件格式的 強大功能。在本文中,做者探討了JAR 格式的許多功能和優點,包括打包、可執行的 JAR 文件、安全性和索引。
JAR 文件是什麼?
JAR 文件格式以流行的 ZIP 文件格式爲基礎,用於將許多個文件彙集爲一個文件。與 ZIP 文件不一樣的是,JAR 文件不只用於壓縮和發佈,並且還用於部署和封裝庫、組件和插件程序,並可被像編譯器和 JVM 這樣的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用來指示工具如何處理特定的 JAR。
一個 JAR 文件能夠用於:
JAR 文件格式提供了許多優點和功能,其中不少是傳統的壓縮格式如 ZIP 或者 TAR 所沒有提供的。它們包括:
壓縮的和未壓縮的 JAR
jar 工具(有關細節參閱 jar 工具 )在默認狀況下壓縮文件。未壓縮的 JAR 文件通常能夠比壓縮過的 JAR 文件更快地裝載,由於在裝載過程當中要解壓縮文件,可是未壓縮的文件在網絡上的下載時間可能更長。
META-INF 目錄
大多數 JAR 文件包含一個 META-INF 目錄,它用於存儲包和擴展的配置數據,如安全性和版本信息。Java 2 平臺識別並解釋 META-INF 目錄中的下述文件和目錄,以便配置應用程序、擴展和類裝載器:
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 文件格式的能力簡化和改進開發和部署過程