1、jar簡介
Java歸檔文件格式(Java Archive, JAR)能夠將多個源碼、資源等文件打包到一個歸檔文件中。這樣,有如下好處:
- 安全性
可以對整個jar包的內容進行簽名。 - 減少了下載時間
如果applet被打包成一個jar文件,那么所有相關的資源就可以在一個HTTP transaction中下載完成,而無需為每一個文件新建一個連接。 - 壓縮
減少了磁盤空間的占用。 - 容易擴展
通過jar這種格式,可以和容易地將自己的程序打包提供給別人使用。 - 包密封(Package Sealing)
存儲在jar文件中的包可以被密封,來保證版本的一致性。密封可以保證一個包中的所有類都來自同一個jar文件。 - 包版本說明
一個jar包可以存儲關于其內容的信息,包括提供商、版本等。 - 可移植性
處理jar文件的機制是Java平臺核心API的標準模塊。
From docs.oracle.com
2、jar的使用
JDK自帶的打包工具通過jar
命令來調用,jar是通過zip格式進行打包的。所以,這個jar工具也可以作為日常的壓縮、解壓縮工具來進行使用。
2.1 jar命令說明
如果安裝了JDK并配置了環境變量,在命令行中輸入jar
命令,不加任何參數,就可以看到jar命令的使用說明,最下面還包含了兩個例子:
jar
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
選項:
-c 創建新檔案
-t 列出檔案目錄
-x 從檔案中提取指定的 (或所有) 文件
-u 更新現有檔案
-v 在標準輸出中生成詳細輸出
-f 指定檔案文件名
-m 包含指定清單文件中的清單信息
-n 創建新檔案后執行 Pack200 規范化
-e 為捆綁到可執行 jar 文件的獨立應用程序指定應用程序入口點
-0 僅存儲; 不使用任何 ZIP 壓縮
-P 保留文件名中的前導 '/' (絕對路徑) 和 ".." (父目錄) 組件
-M 不創建條目的清單文件
-i 為指定的 jar 文件生成索引信息
-C 更改為指定的目錄并包含其中的文件(可以理解為首先cd到指定目錄)
如果任何文件為目錄, 則對其進行遞歸處理。
清單文件名, 檔案文件名和入口點名稱的指定順序與 'm', 'f' 和 'e' 標記的指定順序相同。
示例 1: 將兩個類文件歸檔到一個名為 classes.jar 的檔案中:
jar cvf classes.jar Foo.class Bar.class
示例 2: 使用現有的清單文件 'mymanifest' 并將 foo/ 目錄中的所有文件歸檔到 'classes.jar' 中:
jar cvfm classes.jar mymanifest -C foo/ .
摘自<jar命令的幫助文檔>
2.2 jar使用舉例
jar命令打包時默認會在jar包中添加清單(manifest)文件,如果不想添加,手動指定-M
選項:
>jar -cvf HelloWorld.jar HelloWorld.class #將HelloWorld.class文件打入jar包
已添加清單
正在添加: HelloWorld.class(輸入 = 427) (輸出 = 289)(壓縮了 32%)
>jar -tf HelloWorld.jar #查看歸檔文件的內容
META-INF/
META-INF/MANIFEST.MF
HelloWorld.class
>jar -xf HelloWorld.jar META-INF/MANIFEST.MF #解壓出其中的META-INF/MANIFEST.MF文件
>type META-INF\MANIFEST.MF #查看清單文件的內容
Manifest-Version: 1.0
Created-By: 1.8.0_51 (Oracle Corporation)
>jar -cvfM HelloWorld.jar HelloWorld.class #將HelloWorld.class文件打入jar包,不要添加清單文件
正在添加: HelloWorld.class(輸入 = 427) (輸出 = 289)(壓縮了 32%)
>jar -tf HelloWorld.jar
HelloWorld.class
>jar -xf HelloWorld.jar #解壓tar文件到當前目錄
如果要生成可以運行的jar包,需要指定jar包的應用程序入口點,用-e選項:
>jar -cvfe HelloWorld.jar HelloWorld HelloWorld.class #創建可以運行的jar包
已添加清單
正在添加: HelloWorld.class(輸入 = 427) (輸出 = 289)(壓縮了 32%)
>jar -tf HelloWorld.jar #查看歸檔的內容
META-INF/
META-INF/MANIFEST.MF
HelloWorld.class
>type META-INF\MANIFEST.MF #查看清單文件的內容
Manifest-Version: 1.0
Created-By: 1.8.0_51 (Oracle Corporation)
Main-Class: HelloWorld
>java -jar HelloWorld.jar #運行jar包
Hello World!!
查看jar包的文件列表:
jar vtf fileName.jar
3、清單文件MANIFEST.MF
jar打包時,會默認向jar包中添加一個清單文件(MANIFEST.MF),放在META-INF目錄。上面的例子中,可以看到:如果指定了jar包的入口程序,清單文件中就會多一行Main-Class: HelloWorld
。實際上,-e
選項的作用就是向MANIFEST.MF文件中添加這樣一行信息,來聲明jar包的入口程序。當然,我們也可以直接修改清單文件的內容。
3.1 修改清單文件的內容
前面已經說了,-m
選項可以添加指定清單文件中的清單信息,格式如下:
jar cfm jar-file manifest-addition input-file(s)
注意:
- manifest-addition是一個已經存在的文本文件,其中包含著想要往jar包的清單文件中添加的清單信息。
- manifest-addition的文件編碼格式必須是UTF-8。
- 清單信息每行后面必須有回車或者換行。(The text file from which you are creating the manifest must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return.)
- 清單文件名, 檔案文件名和入口點名稱的指定順序與 'm', 'f' 和 'e' 標記的指定順序相同。(前面已經提到了)
3.2 在清單文件中聲明入口程序
>type manifest.txt #查看清單信息的內容
Main-Class: HelloWorld
>jar -cvfm HelloWorld.jar manifest.txt HelloWorld.class #添加清單信息到jar包
已添加清單
正在添加: HelloWorld.class(輸入 = 427) (輸出 = 289)(壓縮了 32%)
>jar -xvf HelloWorld.jar META-INF\MANIFEST.MF
已解壓: META-INF/MANIFEST.MF
>type META-INF\MANIFEST.MF
Manifest-Version: 1.0
Created-By: 1.8.0_51 (Oracle Corporation)
Main-Class: HelloWorld #可以看到清單信息成功添加
>java -jar HelloWorld.jar #這樣指定入口程序,生成的jar包照樣可以運行。
Hello World!!
3.3 在清單文件中指定CLASSPATH
在運行java
命令的時候,如果指定了-jar
選項,那么環境變量CLASSPATH和在命令行中指定的所有類路徑都被JVM所忽略。這時,如果一個jar包引用了其它的jar包,解決方案有兩個:
java -cp lib\log4j-1.2.14.jar;hello.jar com.dhn.Hello
(com.dhn.Hello為主類)
在windows下多個jar之間以分號(;)隔開,最后還需要指定運行jar文件中的完整的主類名。-
java -jar hello.jar
這種,需要修改hello.jar中的MANIFEST.MF,通過MANIFEST.MF中的Class-Path 來指定運行時需要用到的其他jar,其他jar可以是當前路徑也可以是當前路徑下的子目錄。多個jar文件之間以空格隔開。
以下面的MANIFEST.MF文件為例Manifest-Version: 1.0 Main-Class: com.ibm.portalnews.entrance.Main Class-Path: lib\commons-collections-3.2.jar lib\commons-configuration-1.5.jar lib\commons-lang-2.3.jar lib\commons-logging.jar lib\dom4j-1.6.1.jar lib\jaxen-1.1-beta-7.jar lib\jdom.jar lib\log4j-1.2.14.jar
其中:
- Manifest-Version表示版本號,一般由IDE工具(如eclipse)自動生成
Main-Class 是jar文件的主類,程序的入口
Class-Path 指定需要的jar,多個jar必須要在一行上,多個jar之間以空格隔開,如果引用的jar在當前目錄的子目錄下,windows下使用\來分割,linux下用/分割
文件的冒號后面必須要空一個空格,否則會出錯
文件的最后一行必須是一個回車換行符,否則也會出錯
From: java -jar classpath心得
3.4 將jar包A引用的其它jar包一同打包到A里面
這個問題,是我一直想的。如果這樣,那么將一個程序到處拷貝的話就方便的多了。然而,這是行不通的,因為JDK的類加載器不會加載jar包內包含的其它jar包中的類。
當然,這樣想過的人不止我一個,可以通過一些第三方的工具來完成。比如有個工具叫one-jar,感興趣的可以看下。
3.5 通過清單文件密封包
JAR中的Package Sealing功能是在Java 1.2引入的。在生成Jar文件時我們可以指定是否將整個Jar或者其中某幾個Package進行密封,如果是將Jar文件整個進行密封,那就意味著其內所有的Package都被密封了。Package一旦密封,那么Java 虛擬機一旦成功裝載密封Package中的某個類后,其后所有裝載的帶有相同Package名的類必須來自同一個Jar文件,否則將觸發Sealing Violation安全異常。
對包的密封方式主要有兩種:一種是對jar文件中的某些包進行密封,另一種是密封jar中的所有包。
-
對某些包密封。
Manifest-Version: 1.0 Created-By: 1.8.0_51 (Oracle Corporation) Name: com/test/hello/ Sealed: true Name: com/test/world/ Sealed: true
-
密封jar中的所有包。
Manifest-Version: 1.0 Created-By: 1.8.0_51 (Oracle Corporation) Sealed: true
概括來說,有兩種情況觸發關于Sealing的安全異常:
- 檢查當前試圖加載的類對應的Package是否已經被JVM裝載且密封。如果已經被裝載且密封了,但被密封的Package與當前加載的類對應的Package不是來自同一個Jar文件,將觸發安全異常。
- 檢查當前試圖加載的類對應的 Package是否已經被JVM裝載且密封。如果已經被裝載但沒有被密封,但當前試圖加載的類對應的Package確要試圖進行密封操作,將觸發安全異常。JVM不允許對已經裝載但未密封的Package再進行密封操作。
Package Sealing的好處:
Package Sealing所能帶來的好處主要是版本一致性. 我們知道Java 在運行時是嚴格按照classpath中定義的順序進行裝載和檢查,尤其是現在Java開源包滿天飛, 很有可能你的Java應用程序或者中間件的classpath中會在不同的Jar文件中包含同一個Package的不同版本。這會使得程序運行產生不一致性結果,很難發現。