1. 程式人生 > >java -jar引數執行打包好的jar應用程式的時候發現應用程式無法找到classpath下設定好的第三方類庫?

java -jar引數執行打包好的jar應用程式的時候發現應用程式無法找到classpath下設定好的第三方類庫?

 你是否在使用java -jar引數執行打包好的jar應用程式的時候發現應用程式無法找到classpath下設定好的第三方類庫的內容?無論怎麼設定classpath引數都無濟於事,總是會報ClassNotFound的錯誤?那麼本篇帖子可以幫助你擺脫煩惱 :)
當用java -jar yourJarExe.jar來執行一個經過打包的應用程式的時候,你會發現如何設定-classpath引數應用程式都找不到相應的第三方類,報ClassNotFound錯誤。實際上這是由於當使用-jar引數執行的時候,java VM會遮蔽所有的外部classpath,而只以本身yourJarExe.jar的內部class作為類的尋找範圍。

**解決方案**

一 BootStrap class擴充套件方案

Java 命令列提供瞭如何擴充套件bootStrap 級別class的簡單方法.
-Xbootclasspath:     完全取代基本核心的Java class 搜尋路徑.
                                   不常用,否則要重新寫所有Java 核心class
-Xbootclasspath/a: 字尾在核心class搜尋路徑後面.常用!!
-Xbootclasspath/p: 字首在核心class搜尋路徑前面.不常用,避免
                                   引起不必要的衝突.

語法如下:
 (分隔符與classpath引數類似,unix使用:號,windows使用;號,這裡以unix為例)
 java -Xbootclasspath/a:/usrhome/thirdlib.jar: -jar yourJarExe.jar

二 extend class 擴充套件方案

Java exten class 存放在{Java_home}/jre/lib/ext目錄下.當呼叫Java時,對擴充套件class路徑的搜尋是自動的.總會搜尋的.這樣,解決的方案就很簡單了,將所有要使用的第三方的jar包都複製到ext 目錄下.

三 User class擴充套件方案

當使用-jar執行可執行Jar包時,JVM將Jar包所在目錄設定為codebase目錄,所有的class搜尋都在這個目錄下開始.所以如果使用了其他第三方的jar包,一個比較可以接受的可配置方案,就是利用jar包的Manifest擴充套件機制.
步驟如下:

 1.將需要的第三方的jar包,複製在同可執行jar所在的目錄或某個子目錄下. 比如:jar 包在 /usrhome/yourJarExe.jar 那麼你可以把所有jar包複製到/usrhome目錄下或/usrhome/lib 等類似的子目錄下.

 2.修改Manifest 檔案

 在Manifest.mf檔案里加入如下行

 Class-Path:classes12.jar lib/thirdlib.jar

 Class-Path 是可執行jar包執行依賴的關鍵詞.詳細內容可以參考 http://java.sun.com/docs/books/tutorial/deployment/jar/downman.html 。要注意的是 Class-Path 只是作為你本地機器的CLASSPATH環境變數的一個縮寫,也就是說用這個字首表示在你的jar包執行機器上所有的CLASSPATH目錄下尋找相應的第三方類/類庫。你並不能通過 Class-Path 來載入位於你本身的jar包裡面(或者網路上)的jar檔案。因為從理論上來講,你的jar釋出包不應該再去包含其他的第三方類庫(而應該通過使用說明來提醒使用者去獲取相應的支援類庫)。如果由於特殊需要必須把其他的第三方類庫(jar, zip, class等)直接打包在你自己的jar包裡面一起釋出,你就必須通過實現自定義的ClassLoader來按照自己的意圖載入這些第三方類庫。


以上三種方法推薦第一種,擴充套件性好,操作起來也最方便.
另外編寫自己的ClassLoader,來動態載入class,是更加複雜和高階技術.限於篇幅,不贅述.有興趣瞭解可以去google一下custom classloader,或者參考我的另一篇日誌:讓classpath引數走開。

Java的安全機制隨不同的JDK版本有不同的變化,會影響很多核心CLASS,比如Thread,所以很多大型商業軟體,要求JDK的版本很嚴格.部分原因也在此.這也要求在釋出自己編寫的應用時候,不管大小,都要說明開發和測試的JDK版本.


本文所述方法測試基於j2sdk 1.4.2_04-b05

----------------------------------------------------------------------------------------------

附:背景知識

歸納來講:是基於JVM sandbox(沙盒)安裝模型上提供應用層的可定製的安全機制.


Java虛擬機器(JVM)尋找Class的順序

1. Bootstrap classes

屬於Java 平臺核心的class,比如java.lang.String等.及rt.jar等重要的核心級別的class.這是由JVM Bootstrap class loader來載入的.一般是放置在{java_home}/jre/lib目錄下

2. Extension classes

基於Java擴充套件機制,用來擴充套件Java核心功能模組.比如Java串列埠通訊模組comm.jar.一般放置在{Java_home}/jre/lib/ext目錄下

3. User classes

開發人員或其他第三方開發的Java程式包.通過命令列的-classpath或-cp,或者通過設定CLASSPATH環境變數來引用.JVM通過放置在{java_home}/lib/tools.jar來尋找和呼叫使用者級的class.常用的javac也是通過呼叫tools.jar來尋找使用者指定的路徑來編譯Java源程式.這樣就引出了User class路徑搜尋的順序或優先級別的問題.

 3.1 預設值:呼叫Java或javawa的當前路徑(.),是開發的class所存在的當前目錄
 3.2 CLASSPATH環境變數設定的路徑.如果設定了CLASSPATH,則CLASSPATH的值會覆蓋預設值
 3.3 執行Java的命令列-classpath或-cp的值,如果制定了這兩個命令列引數之一,它的值會覆蓋環境變數CLASSPATH的值
 3.4 -jar 選項:如果通過java -jar 來執行一個可執行的jar包,這當前jar包會覆蓋上面所有的值.換句話說,-jar 後面所跟的jar包的優先級別最高,如果指定了-jar選項,所有環境變數和命令列制定的搜尋路徑都將被忽略.JVM APPClassloader將只會以jar包為搜尋範圍.
有關可執行jar有許多相關的安全方面的描述,可以參考http://java.sun.com/docs/books/tutorial/jar/ 來全面瞭解.

這也是為什麼應用程式打包成可執行的jar包後,不管你怎麼設定classpath都不能引用到第三方jar包的東西了.