如何構造使用自訂的ClassLoader
既然自訂的ClassLoader,能解決上述問題,那接下去看看,我們如何來使用自訂的ClassLoader。
結合本文種的原碼---(在differentversionspush的目錄裡),有個FileSystemClassLoader,類圖描述如下:
圖9.
看看他的方法 findClassBytes(String className);
public byte[] findClassBytes(String className){
try{
String pathName = currentRoot +
File.separatorChar + className.
replace('.', File.separatorChar)
+ ".class";
FileInputStream inFile = new
FileInputStream(pathName);
byte[] classBytes = new
byte[inFile.available()];
inFile.read(classBytes);
return classBytes;
}
catch (java.io.IOException ioEx){
return null;
}
}
public Class findClass(String name)throws
ClassNotFoundException{
byte[] classBytes = findClassBytes(name);
if (classBytes==null){
throw new ClassNotFoundException();
}
else{
return defineClass(name, classBytes,
0, classBytes.length);
}
}
public Class findClass(String name, byte[]
classBytes)throws ClassNotFoundException{
if (classBytes==null){
throw new ClassNotFoundException(
"(classBytes==null)");
}
else{
return defineClass(name, classBytes,
0, classBytes.length);
}
}
public void execute(String codeName,
byte[] code){
Class klass = null;
try{
klass = findClass(codeName, code);
TaskIntf task = (TaskIntf)
klass.newInstance();
task.execute();
}
catch(Exception exception){
exception.printStackTrace();
}
}
這個類FileSystemClassLoader 被client使用了,用來定義class, 並且把它把client.TaskImpl(v1)轉化為 byte[], 然後 byte[]發送到RMI Server執行。(上面講了defineClass()能夠執行任何位元組碼,來自編譯後的檔案,網路甚至是BCEL 位元組碼引擎庫), 在Server端 ,又可以通過FileSystemClassLoader 以為byte[]的形式定義出 client.TaskImpl。
請看Client端的代碼:
public class Client{
public static void main (String[] args){
try{
byte[] code = getClassDefinition
("client.TaskImpl");
serverIntf.execute("client.TaskImpl",
code);
}
catch(RemoteException remoteException){
remoteException.printStackTrace();
}
}
private static byte[] getClassDefinition
(String codeName){
String userDir = System.getProperties().
getProperty("BytePath");
FileSystemClassLoader fscl1 = null;
try{
fscl1 = new FileSystemClassLoader
(userDir);
}
catch(FileNotFoundException
fileNotFoundException){
fileNotFoundException.printStackTrace();
}
return fscl1.findClassBytes(codeName);
}
}
在RMI伺服器端ServerImpl 程式裡, 接受到來自client的位元組碼(byte[]),於是FileSystemClassLoader 會從byte[]構造出一個class, 執行個體話,並且執行。
有一點要注意:每次接收到一個client的請求,FileSystemClassLoader都會重新執行個體化(執行結果中可以看出來),這就意味著,client.Impl不在是在classpath中被找到的,而是通過FileSystemClassLoader 的findClass() 來執行deFineClass(),這樣每次 FileSystemClassLoader 都是建立新的執行個體,,自然 deFine出來的class也是不同的。 這樣,我們就能在RMI的執行中區分出 這兩個class來。(client.TaskImpl != client.TaskImp 在上篇就已經得出結論了。 )
看看伺服器端的執行代碼:
public void execute(String codeName, byte[] code)throws RemoteException{
FileSystemClassLoader fileSystemClassLoader = null;
try{
fileSystemClassLoader = new FileSystemClassLoader();
fileSystemClassLoader.execute(codeName, code);
}
catch(Exception exception){
throw new RemoteException(exception.getMessage());
}
}
伺服器端的執行結果:
圖10,伺服器端顯示
下面兩圖分別是用戶端顯示的。
圖11. client1的執行顯示
圖12. client2執行結果
哈,上面洋洋洒洒那麼多,總算是一步一步的教會了大家 如何在同一個VM虛擬機器中,執行“不同版本”的代碼 。(這些代碼有同樣的類名和包名)。
Class Loaders 在 J2EE 中應用。
到這裡你其實已經不足為奇下面一些東西了。。。
我的一個A_war.war的web項目中 代碼是 com.mycom.Test 而我在另外一個B_war.war的wenb項目中的 代碼也是com.mycom.Test 而他們照樣工作的好好的。
當一個大型的 EJB項目,一台伺服器上部署了多個 EJB,War工程時候,他們也不會互相影響。AppServer還會有自己的裝載策略,比如你web中用的jar包,會優先於AppServer本身所帶有的。
另外,J2EE的ClassLoader機制更詳細的能容你可以參考Tss上的這篇文章。
Understanding J2EE Application Server Class Loading Architectures
"
資源檔:
Sample code for this article
JDK 1.5 API Docs
The Java language specification
"Understanding Extension Class Loading " in the Java tutorial
"Inside Class Loaders" from ONJava
"Inside Class Loaders: Debugging" from ONJava
"What version is your Java code?" from JavaWorld
" Understanding J2EE Application Server Class Loading Architectures" from TheServerSide
Byte Code Engineering Library
Server-Based Java Programming by Ted Neward
原文:
http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.htm