我們是否需要自訂的ClassLoader? 理由之一: 如果我們自訂了ClassLoader,那我們便可以控制JVM的載入動作了。
上面說一個class標識是由於package+classname組成得。 對於所有實現java.io.Serializable介面得類, 由serialVersionUID管理這些類得版本(RMI,JNDI,Security裡都有這樣一個ID) 。它用64位的Hash來表示 (由於classname,filed,method組成)。從技術上講如果classname,field,mehtod所構成的Hash都一樣,那就會認為是同一個版本。
假設有這樣一個情況,我們要寫一個java 執行引擎(比如:用一個RMI 發布一個Server端程式,執行client的介面), 既然要能執行,那引擎肯定要實現有Client所特定任務的介面(這裡為TaskIntf)。 一但任務提交給執行引擎,Server要做的第一件事情就是要裝載所有要執行的代碼。 假設不同的終端遞交了不同的代碼。而偏偏又都是同樣的包名,和同樣的類名。 那伺服器能否會辨別到底是那個Client提交過來的執行請求? 現在出個問題: 如果在伺服器端一個執行程式執行兩個用戶端提交同一個版本得代碼,如何才讓用戶端會得到預期的執行結果? 別以為這個很簡單,下面我先建個RMI玩玩。看看結果會是怎樣。 答案就在本文得代碼裡(這時候我也想知道:) 本地檔案如。
圖 2 程式目錄結構 (本文中含代碼)。 在samepath目錄下, 有著兩個version.Version.class,他們得包名類名都一樣,唯一不同的是。v1目錄中的方法是:
public void fx(){
log("this = " + this + "; Version.fx(1).");
}
v2目錄中的方法是: public void fx(){
log("this = " + this + "; Version.fx(2).");
} 執行一下看看:set CLASSPATH=.;%CURRENT_ROOT%/v1;%CURRENT_ROOT%/v2
%JAVA_HOME%/bin/java Test 結果如
圖 3. classPath得目錄設為v1
切換到set CLASSPATH=.;%CURRENT_ROOT%/v2;%CURRENT_ROOT%/v1
%JAVA_HOME%/bin/java Test
結果如:
圖 4. classpath目錄設為v2
很明顯,上面的例子中能從classpath中找到先後次序。如果我們把v1,v2的version.Version。都刪調。而把他們打成一個myextension.jar包,放到java.ext.dirs目錄下。。這時候就通過ExtClassLoader來裝載了,而不是AppClassLoader.
那結果會是下面得:
圖 5. AppClassLoader and ExtClassLoader
注意看 sun.misc.Launcher$ExtClassLoader@a9c85c 這下是有ExtClassLoader 載入了。
繼續往下看,另外一個例子。 在differentversions 目錄下的例子,裡麵包含了RMI的ServerImpl這樣一個執行引擎。Client實現了common.TaskIntf介面。 兩個 client.TaskImpl分別如下:
static{
log("client.TaskImpl.class.getClassLoader
(v1) : " + TaskImpl.class.getClassLoader());
}
public void execute(){
log("this = " + this + "; execute(1)");
}
另一個則是:
static{
log("client.TaskImpl.class.getClassLoader
(v1) : " + TaskImpl.class.getClassLoader());
}
public void execute(){
log("this = " + this + "; execute(2)");
}
這樣子來執行(順序隨便,這裡把 %CURRENT_ROOT%/client2放在前面):
CLASSPATH=%CURRENT_ROOT%/common;%CURRENT_ROOT%/server;
%CURRENT_ROOT%/client2;%CURRENT_ROOT%/client1
%JAVA_HOME%/bin/java server.Server
先啟動Server..
分別把兩個client提交給伺服器執行, (即便執行程式中得client1.bat 和 client2.bat server監控螢幕6所示。)
圖 6. Execution Engine Server console
在來看下面兩個圖(圖7和圖8),分別是client端得執行反映。
圖 7. Execution Engine Client 1 console
圖 8. Execution Engine Client 2 console
縱觀上面三次執行結果,發現由於伺服器啟動得時候使用了AppClassLoader.所以無論怎麼樣都是載入得是client2(因為client2的classpath次序比較在前),
這裡client1 很鬱悶,它在自己那執行明明是 execute(1) 通過 RMI 發送給伺服器端執行就成了 execute(2)..
值得注意的是: 在client1,client2分別發送給伺服器執行之後,伺服器端顯示的記錄是:
client.TaskImpl.class.getClassLoader(v2):sun.misc.lancuher@AppClassLoader@xxxx zhiz只執行了一次。而this=client.TaskImpl@xxxxx execute(2);執行了兩次
上面已經講到過了,對於一個ClassLoader來講 同樣的page+className 只能定義一個 class,而不同的ClassLoader即便載入同一個page.className 也會定義不同的class
到這裡,我才發現,解決上面提出得那個問題似乎並不容易。:(。
那如何解決呢?答案就是---使用自訂得classLoader ..
如果各位等不急的話, 先請看(目錄中 differentversionspush 裡面的代碼)
很顯然,我們很有必要寫自訂的classloader.
圖 7. Execution Engine Client 1 console
圖 8. Execution Engine Client 2 console
縱觀上面三次執行結果,發現由於伺服器啟動得時候使用了AppClassLoader.所以無論怎麼樣都是載入得是client2(因為client2的classpath次序比較在前),
這裡client1 很鬱悶,它在自己那執行明明是 execute(1) 通過 RMI 發送給伺服器端執行就成了 execute(2)..
值得注意的是: 在client1,client2分別發送給伺服器執行之後,伺服器端顯示的記錄是:
client.TaskImpl.class.getClassLoader(v2):sun.misc.lancuher@AppClassLoader@xxxx zhiz只執行了一次。而this=client.TaskImpl@xxxxx execute(2);執行了兩次
上面已經講到過了,對於一個ClassLoader來講 同樣的page+className 只能定義一個 class,而不同的ClassLoader即便載入同一個page.className 也會定義不同的class
到這裡,我才發現,解決上面提出得那個問題似乎並不容易。:(。
那如何解決呢?答案就是---使用自訂得classLoader ..
如果各位等不急的話, 先請看(目錄中 differentversionspush 裡面的代碼)
很顯然,我們很有必要寫自訂的classloader.
圖 8. Execution Engine Client 2 console
縱觀上面三次執行結果,發現由於伺服器啟動得時候使用了AppClassLoader.所以無論怎麼樣都是載入得是client2(因為client2的classpath次序比較在前),
這裡client1 很鬱悶,它在自己那執行明明是 execute(1) 通過 RMI 發送給伺服器端執行就成了 execute(2)..
值得注意的是: 在client1,client2分別發送給伺服器執行之後,伺服器端顯示的記錄是:
client.TaskImpl.class.getClassLoader(v2):sun.misc.lancuher@AppClassLoader@xxxx zhiz只執行了一次。而this=client.TaskImpl@xxxxx execute(2);執行了兩次
上面已經講到過了,對於一個ClassLoader來講 同樣的page+className 只能定義一個 class,而不同的ClassLoader即便載入同一個page.className 也會定義不同的class
到這裡,我才發現,解決上面提出得那個問題似乎並不容易。:(。
那如何解決呢?答案就是---使用自訂得classLoader ..
如果各位等不急的話, 先請看(目錄中 differentversionspush 裡面的代碼)
很顯然,我們很有必要寫自訂的classloader.