如何构造使用自定义的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本身所带有的。
原文: http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.htm 
|