天天看點

淺談java程式釋出之 JRE篇

Java開發程式,釋出時總要考慮的問題就是怎麼在使用者的機器上裝好JRE。要考慮的問題很多:使用者有沒有能力獨自安裝JRE,使用者已有的 JRE 和我們需要的版本是不是一緻,會不會出現版本問題,等等。使用.NET要考慮的問題就少些。現在.NET CLR似乎已經很普及了,看好多D版的 Win XP都會自己安裝最新的.NET CLR,而且似乎它的安裝界面也比JRE友好些。徹底解決安裝JRE的問題的方案,就是讓我們的應用程式自己背着JRE!這樣,我們的程式就像傳統的Win32應用程式一樣,輕按兩下就可以執行,不用管所在的機器上是否有JRE,是什麼版本的JRE,無論怎樣,我有我自己的!要做到這一點,其實非常容易。

王森在他的《Java深度曆險》(強力推薦這本書,内容少而精)的第一章就解釋了JDK,JRE,JVM之間的關系。解釋了我們執行java.exe時發生的事情。其中提到,java.exe依照一套邏輯來尋找可以用的JRE,

1、首先查找自己所在的目錄下有沒有 JRE(據王森講這樣說不确切,我沒有JDK全部的源代碼,在此無從考證);(注:經過我的實際驗證,在windows2003+jdk 6的條件下,java.exe不會在自己的目前目錄下尋找JRE目錄)

2、其次查找自己的父目錄下有沒有JRE;(注:當時實驗時就這個問題郁悶了半天,原因是開始我把JRE了解為泛的java運作環境概念,而java.exe需要用的内容都在/JRE/LIB目錄下,是以我把這個目錄的内容放在父目錄下,但是不起作用。後來才知道,JRE是固定的目錄名稱而且必須使用這個名稱,并且必須将JRE安裝目錄下的内容都複制過來才有效。也就是說,java.exe必須要在.../windows/底下找到JRE目錄,否則就按第3的邏輯找系統資料庫。sign :-(

3、最後才是查詢Windows的系統資料庫(注:HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment)。

注1:思維發散---我這個人考慮問題總是這樣,容易渙散。java.exe的版本必須和他找到的JRE版本相比對,否則會有錯誤。例如,安裝了JDK 6,在console下鍵入java -jar -verbose pw.zfb.jar, 自然就會用system32目錄下的java.exe執行, 并且在目前目錄和父目錄下沒有找到JRE,于是察看系統資料庫發現,CurrentVersion的值是1.4(該值可能被修改了),于是繼續在1.4的鍵值中找到JavaHome,從他的值中得到JRE的路徑,于是,1.6版的java使用了1.4版的JRE,so, error is occured. as follow:

C:\javatest>java -jar -verbose pw.zfb.jar >tt.txt

Registry key 'Software\JavaSoft\Java Runtime Environment\CurrentVersion'

has value '1.4', but '1.6' is required.

Error: could not find java.dll

Error: could not find Java 2 Runtime Environment.

同理,如果你釋出的java程式中自帶了JRE,請務必保證java.exe和JRE配套運作。這樣也避免了JRE不一緻導緻的問題。是以,開發Java程式或是執行Java 程式時,一定要記得兩件事:

隻要這兩件事情确定了,就知道問題發生的來龍去脈,也可以很容易地解決很多貌似的怪異的問題。

注2:思維發散---上面查找規則中說的“首先查找自己所在的目錄下”中,自己所在的目錄指java.exe所在的目錄,比如,我們打開console,進入c:\根目錄下,在console中直接鍵入c:\java.exe,如果c:的根目錄下沒有java.exe, 那麼console怎麼找到java.exe呢,肯定是通過周遊console的環境變量的path路徑去查找比對。那麼我們目前的工作路徑是c:\,但是,java.exe尋找JRE的規則中,是查找自己所在的目錄和父目錄(也就是java.exe所在的路徑),而不是在目前工作路徑下尋找。而對于[JDK 5.0以上預設到目前工作目錄,以及JDK的lib目錄中尋找Java

L:\eclipse\workspace\JavaTest\arrays\IceCream.class 中使用了L:\eclipse\workspace\JavaTest\pw\zfb\marry.class中的類,如果我們在L:\eclipse\workspace\JavaTest\ 目錄下鍵入 java arrays.IceCream   那麼執行沒有問題,因為按照[java.exe在JDK 5.0以上預設到目前工作目錄,以及JDK的lib目錄中尋找Java程式]這個規則他在目前工作目錄下找到了marry.class。但是如果我将IceCream.class 換個位置C:\javatest\arrays\IceCream.class ,那麼我在 c:\javatest下鍵入 java arrays.IceCream   就會說找不到marry.class. 這是設定classpath即可解決問題。C:\>set classpath=%classpath%;L:\eclipse\workspace\JavaTest。 如果要放在lib目錄下,就最好先打包成jar檔案,然後再設定好classpath,也可以。(可參考:)

      通常我們在安裝好了JRE的機器上的任何一個目錄下都可以執行java.exe。因為它在安裝時被複制到了windows的system32目錄下,而後者無論如何都會在path環境變量中。這個java.exe最終必然會通路系統資料庫來确定真正的JRE的所在地(因為,從windows xp開始,ms宣布不再在OS中支援java,也就是不再在OS中提供JRE,是以,正常情況下,system32目錄下隻有java.exe,而沒有JRE目錄及相關内容,上層windows目錄下也沒有JRE目錄及相關内容,是以,在他的目前目錄(system32)中,以及父目錄中(windows)無法找到JRE目錄,安裝上面總結的尋找邏輯java.exe隻能去系統資料庫中尋找)。若我們要求每一個應用程式都自帶JRE,必然不能走這條路。但,邏輯的第二條講,java.exe會在它的父目錄下查找JRE,解決方案就在這一條中。

假設我們的應用程式打好了包,叫做 MyApp.jar,放在MyApp的目錄下。我們在MyApp目錄下,可以執行java ?jar MyApp.jar來運作我們的程式。我們安裝的是 JRE 1.5,在C:\Program Files\Java\jre1.5.0下。現在,我們隻需要簡單的将jre1.5.0目錄搬到MyApp目錄下,順便改個容易寫的名字比如叫jre。現在,我們的應用程式的目錄結構就象這樣:

.../MyApp/

    --MyApp.jar

     --Jre

其中jre中是Jre1.5.0目錄下的全部内容。

Java.exe 就在jre目錄下的bin目錄中。根據第二條邏輯,java.exe會在它的父目錄中查找jre,實驗證明,它會查找lib目錄,而lib就在jre目錄下。是以,這樣java.exe就會确定jre的所在然後正常執行java程式,不會去管我們是否安裝了JRE,系統資料庫中是否有注冊項這些雜事了。

試一下,在指令行下進入MyApp的目錄下,假設它在C槽,将path指向MyApp下的JRE:

set path=c:\MyApp\jre\bin   (這時已經将原path中的設定清空了,不會再到windows\system32下尋找,如果不清空,那麼按照尋找次序,還是會先使用windows\system32下的java.exe。是以,一般情況下,自己的程式運作之前最好先在批處理檔案裡面臨時設定PATH,把自己用的JRE放到PATH路徑最前面,是以肯定會運作自己帶的JRE,不會造成版本混亂。 )

然後運作:

java -verbose -jar MyApp.jar

加上verbose參數以确定我們确實用了這一套被搬出了家的JRE。

程式可以運作,并且在指令行輸出的前幾行,可以看到:

[Opened C:\MyApp\jre\lib\rt.jar]

[Opened C:\MyApp\jre\lib\jsse.jar]

[Opened C:\MyApp\jre\lib\jce.jar]

[Opened C:\MyApp\jre\lib\charsets.jar]

是以程式讀取的确實是它的私有的JRE。

至此,我們似乎完成了任務。但是現在我們的私有JRE仍不完美,缺點是太大。JRE 1.5有接近70MB,作為我們的私有的JRE,好多内容都是可以抛棄的。Jre目錄下的license都可以不要,bin下的執行檔案隻需要保留java.exe或者javaw.exe,lib下隻要保留rt,jsse, jce,charsets幾個庫就可以了。除了i386和zi兩個子目錄外,其餘的子目錄都可以不要。Zi下隻需要保留自己地區的子目錄和其下的一些檔案就可以。Lib下除了庫之外的屬性檔案等等都要保留。這樣清理一番,JRE仍然有接近50MB。還可以繼續清理幾個庫檔案裡面不需要的内容,這需要仔細的整理,會很費功夫。最好能寫出一個自動工具幫助我們整理它們。從Sun公司上下到的JMF裡面附帶的用Java寫的媒體播放器就自帶了JRE,隻有幾個 MB。

清理過後需要運作幾遍我們的應用程式,以確定我們的JRE不缺少東西。

如果我們希望能有一個程式直接啟動我們的應用程式,那就還要費些功夫。最簡單的方法是弄出一個快捷方式來,但是快捷方式的路徑不能是相對的,不友善我們安裝。我想到的方案就是用Win32程式包裝一下。在VS.NET下寫一個Win32小程式:

int PASCAL WinMain( HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpszCmdLine,

int nCmdShow )

{

STARTUPINFO si;

PROCESS_INFORMATION pi;

ZeroMemory( &si, sizeof(si) );

si.cb = sizeof(si);

ZeroMemory( π, sizeof(pi) );

// Start the child process.

if( !CreateProcess( "jre\\bin\\javaw.exe",//執行的程式名,該Win32小程式是和MyApp.jar放在一個目錄下

                                                               的,是以,這裡使用相對路徑可以指定使用自己的JRE。

                                                                同時注意,該win32程式必須在包裝的路徑(即MyApp\)中運作才

                                                               有效, 因為他用的是相對路徑。有人問為何不用系統資料庫?

                                                               首先,這個程式的釋出是沒有安裝的過程的,那麼我怎麼知道使用者

                                                               把它放在什麼路經下? 如果僅是針對windows平台釋出,那麼

                                                               MyApp.jar 是我們真正的java程式他保證了平台無關性,而針對

                                                               windows平台的釋出,我們可以使用一個win32 wrapper,設定

                                                                 系統資料庫、環境變量等win32平台特有的配置來解決

                                                               anywhere runable的問題。

"jre\\bin\\javaw.exe -jar MyApp.jar", // 帶參數的執行程式

NULL, // Process handle not inheritable.

NULL, // Thread handle not inheritable.

FALSE, // Set handle inheritance to FALSE.

0, // No creation flags.

NULL, // Use parent's environment block.

NULL, // Use parent's starting directory.

&si, // Pointer to STARTUPINFO structure.

π ) // Pointer to PROCESS_INFORMATION structure.

)

{

ErrorExit( "CreateProcess failed." );

}

// Wait until child process exits.

WaitForSingleObject( pi.hProcess, INFINITE );

// Close process and thread handles.

CloseHandle( pi.hProcess );

CloseHandle( pi.hThread );

}

基本上是按照MSDN文檔中的例子照搬的。将它編譯成一個EXE檔案,我們的任務才全部完成。輕按兩下這個EXE檔案,我們的程式啟動了,看起來和傳統的Win32程式沒有兩樣,JRE完全被隐藏在底層。

繼續閱讀