动态重新加载Class机制之代码测试

V1.1
修改了主程序,调用带参数的构造函数

[java]  view plain copy
  1. package com.ailk;  
  2.   
  3. import java.lang.reflect.*;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6. import com.ailk.dynamic.Demo;  
  7. public class DemoMain {  
  8.     static public void main( String args[] ) throws Exception {  
  9.         String progClass ="com.ailk.dynamic.Demo";  
  10.         // 创建CompilingClassLoader  
  11.         Class c = Class.forName(progClass, truenew CompilingClassLoader());  
  12.         //DemoInterface i=(DemoInterface)c.newInstance();  
  13.   
  14.         //cl1和cl2是两个不同的ClassLoader  
  15.         ClassLoader cl1=c.getClassLoader();  
  16.         ClassLoader cl2=Demo.class.getClassLoader();  
  17.         ClassLoader cl3=DemoInterface.class.getClassLoader();  
  18.   
  19.         int ii=0;  
  20.         List<DemoInterface> objList=new ArrayList();  
  21.         while(true){  
  22.             ii++;  
  23.             CompilingClassLoader ccl = new CompilingClassLoader();  
  24.             // 通过CCL加载主函数类。  
  25.             Class clas = ccl.loadClass( progClass,true);  
  26.             try{  
  27.                 Constructor c1=c.getDeclaredConstructor(new Class[]{String.class});   
  28.                 c1.setAccessible(true);     
  29.                 DemoInterface a1=(DemoInterface)c1.newInstance(new Object[]{"Demo"});  
  30.             }catch(NoSuchMethodException e){  
  31.                 System.out.println("构造函数不存在");  
  32.                 e.printStackTrace();  
  33.             }  
  34.   
  35.             DemoInterface instance=null;  
  36.             try{  
  37.                 Constructor c0=clas.getDeclaredConstructor();  
  38.                 c0.setAccessible(true);     
  39.                 instance=(DemoInterface)c0.newInstance();    
  40.             }catch(NoSuchMethodException e){  
  41.                 System.out.println("构造函数不存在");  
  42.                 e.printStackTrace();  
  43.             }  
  44.   
  45.             ccl=null;//这里讲主动释放cc1  
  46.             //DemoInterface instance=(DemoInterface)clas.newInstance();  
  47.             if (instance!=null)  
  48.             {  
  49.                 objList.add(instance);  
  50.                 instance.print("demo");  
  51.                 // 利用反射调用它的函数和传递参数。  
  52.                 // 产生一个代表主函数的参数类型的类对象。  
  53.                 Class mainArgType[] = { String.class };  
  54.                 //在类中找函数。  
  55.                 Method method = clas.getMethod( "print", mainArgType );  
  56.                 Object[] argsArray={"Demo"};  
  57.                 //调用方法。  
  58.                 method.invoke(instance, argsArray );                  
  59.   
  60.             }  
  61.             if (ii>20)  
  62.             {  
  63.                 ii=0;  
  64.                 objList.clear();  
  65.             }  
  66.             Thread.sleep(500);  
  67.             //强制gc,只有objList清空后 CompilingClassLoader的实例才会释放。  
  68.             //因为只有由CompilingClassLoader载入的class的实例全部释放后,CompilingClassLoader才能被释放  
  69.             System.gc();  
  70.   
  71.         }  
  72.     }  
  73. }  



V1.0
在运行过程住动态编译并重新加载Class 需要继承ClassLoader ,没有不同的CompilingClassLoader
加载的class是不相同的不能相互转换。
对于CompilingClassLoader 的实例,只是简单的赋null是会自动释放的,只有由其载入的class的全部实例都释放后,
CompilingClassLoader 的实例才会释放。
我在代码里加入了protected void finalize()来检测实例何时释放。
 
 
在reload/com/ailk/dynamic中放一个要动态重载的Java程序,这里我们把测试类放这个目录。
代理类
package com.ailk;

public  interface DemoInterface {
public  void print( String args);
}
测试类
package com.ailk.dynamic;
import com.ailk.DemoInterface;
public  class Demo  implements DemoInterface {
@Override
public  void print( String args){
}
}
主程序
package com.ailk;

import java.lang.reflect. *;
import java.util.ArrayList;
import java.util.List;

import com.ailk.dynamic.Demo;


public  class DemoMain {
static  public  void main( String args[] )  throws Exception {



String progClass  = "com.ailk.dynamic.Demo";

// 创建CompilingClassLoader
Class c  = Class.forName(progClass, true,  new CompilingClassLoader());
DemoInterface i =(DemoInterface)c.newInstance();

//cl1和cl2是两个不同的ClassLoader
ClassLoader cl1 =c.getClassLoader();
ClassLoader cl2 =Demo. class.getClassLoader();
ClassLoader cl3 =DemoInterface. class.getClassLoader();


int ii = 0;
List <DemoInterface > objList = new ArrayList();
while(true){
ii ++;
CompilingClassLoader ccl  =  new CompilingClassLoader();
// 通过CCL加载主函数类。
Class clas  = ccl.loadClass( progClass,true);
// 利用反射调用它的函数和传递参数。
// 产生一个代表主函数的参数类型的类对象。
Class mainArgType[]  = { String. class };
// 在类中找函数。
Method method  = clas.getMethod(  "print", mainArgType );
Object[] argsArray ={ "Demo"};
//调用方法。
method.invoke( clas.newInstance(), argsArray );

ccl =null; //这里讲主动释放cc1
DemoInterface instance =(DemoInterface)clas.newInstance();
objList.add(instance);
instance.print( "demo");
if (ii > 20)
{
ii = 0;
objList.clear();
}
Thread.sleep( 500);
//强制gc,只有objList清空后 CompilingClassLoader的实例才会释放。
//因为只有由CompilingClassLoader载入的class的实例全部释放后,CompilingClassLoader才能被释放
System.gc();

}
}
}
下面的这个类就是我们自己的一个CompilingClassLoader 。其功能是从当前类的路径中的目录的reload中加载class文件,如果其Java文件有更新则重新编译然后再加载,注意,在reload的目录中只放如需要重新加载的类,其他的不要放到这个目录中,特别是代理接口。
如果代理接口也放到这个目录中的相应的目录下的话,CompilingClassLoader 就会将其加载,那么 我们在main程序的DemoInterface instance=(DemoInterface)clas.newInstance()行就会发生java.lang.ClassCastException 错误。

[java]  view plain copy
  1. package com.ailk;  
  2.   
  3. import java.io.*;  
  4.   
  5. /* 
  6.  CompilingClassLoader动态的编译Java源文件。它检查.class文件是否存在,.class文件是 
  7.  否比源文件陈旧。 
  8.  */  
  9. public class CompilingClassLoader extends ClassLoader {  
  10.     protected void finalize()  
  11.     {  
  12.         System.out.println("finalize this:"+this);  
  13.         try {  
  14.             super.finalize();  
  15.         } catch (Throwable e) {  
  16.             // TODO Auto-generated catch block  
  17.             e.printStackTrace();  
  18.         }  
  19.   
  20.      }  
  21.     // 指定一个文件名,从磁盘读取整个文件内容,返回字节数组。  
  22.     private byte[] getBytes(String filename) throws IOException {  
  23.         // 获得文件大小。  
  24.         File file = new File(filename);  
  25.         long len = file.length();  
  26.         // 创建一个数组刚好可以存放文件的内容。  
  27.         byte raw[] = new byte[(int) len];  
  28.         // 打开文件  
  29.         FileInputStream fin = new FileInputStream(file);  
  30.         // 读取所有内容,如果没法读取,表示发生了一个错误。  
  31.         int r = fin.read(raw);  
  32.         if (r != len)  
  33.             throw new IOException("Can't read all, " + r + " != " + len);  
  34.         // 别忘了关闭文件。  
  35.         fin.close();  
  36.         // 返回这个数组。  
  37.         return raw;  
  38.     }  
  39.   
  40.     // 产生一个进程来编译指定的Java源文件,制定文件参数.如果编译成功返回true,否者,  
  41.     // 返回false。  
  42.     private boolean compile(String javaFile) throws IOException {  
  43.         // 显示当前进度  
  44.         System.out.println("CCL: Compiling " + javaFile + "...");  
  45.         // 启动编译器  
  46.         Process p = Runtime.getRuntime().exec(  
  47.                 "javac -classpath " + CompilingClassLoader.class.getResource("/").getPath()  
  48.                         + " -Xlint:unchecked " + javaFile);  
  49.         // 等待编译结束  
  50.         try {  
  51.             p.waitFor();  
  52.         } catch (InterruptedException ie) {  
  53.             System.out.println(ie);  
  54.         }  
  55.         // 检查返回码,看编译是否出错。  
  56.         int ret = p.exitValue();  
  57.         // 返回编译是否成功。  
  58.         return ret == 0;  
  59.     }  
  60.   
  61.     // 类加载器的核心代码 -加载类在需要的时候自动编译源文件。  
  62.     public Class loadClass(String name, boolean resolve)  
  63.             throws ClassNotFoundException {  
  64.         // if (!name.startsWith("com.ailk.dynamic")){  
  65.         // return getParent().loadClass(name);  
  66.         // }  
  67.         // 我们的目的是获得一个类对象。  
  68.         Class clas = null;  
  69.         // 首先,检查是否已经出理过这个类。  
  70.         clas = findLoadedClass(name);  
  71.         if (clas != null)  
  72.             return clas;  
  73. //      if (clas == null) {  
  74. //          try {  
  75. //              if (getParent() != null) {  
  76. //                  clas = super.findClass(name);  
  77. //              } else {  
  78. //                  clas = findSystemClass(name);  
  79. //              }  
  80. //          } catch (ClassNotFoundException e) {  
  81. //              // If still not found, then invoke findClass in order  
  82. //              // to find the class.  
  83. //              //clas = findClass(name);  
  84. //          }  
  85. //      }  
  86.   
  87.         // System.out.println( "findLoadedClass: "+clas );  
  88.         // 通过类名获得路径名 比如:java.lang.Object => java/lang/Object  
  89.         String fileStub = name.replace('.''/');  
  90.         // 构建指向源文件和类文件的对象。  
  91.         String javaFilename = CompilingClassLoader.class.getResource("/").getPath() + "reload/"  
  92.                 + fileStub + ".java";  
  93.         //System.out.println(javaFilename);  
  94.         String classFilename = CompilingClassLoader.class.getResource("/").getPath() + "reload/"  
  95.                 + fileStub + ".class";  
  96.         //System.out.println(classFilename);  
  97.         File javaFile = new File(javaFilename);  
  98.         File classFile = new File(classFilename);  
  99.         // System.out.println( "j "+javaFile.lastModified()+" c "  
  100.         // +classFile.lastModified() );  
  101.         // 首先,判断是否需要编译。如果源文件存在而类文件不存在,或者都存在,但是源文件  
  102.         // 较新,说明需要编译。  
  103.         boolean javaExists = javaFile.exists();  
  104.         boolean classExists = classFile.exists();  
  105.         if (javaFile.exists()  
  106.                 && (!classFile.exists() || javaFile.lastModified() > classFile  
  107.                         .lastModified())) {  
  108.             try {  
  109.                 // 编译,如果编译失败,我们必须声明失败原因(仅仅使用陈旧的类是不够的)。  
  110.                 if (!compile(javaFilename) || !classFile.exists()) {  
  111.                     throw new ClassNotFoundException("Compile failed: "  
  112.                             + javaFilename);  
  113.                 }  
  114.             } catch (IOException ie) {  
  115.                 // 可能编译时出现IO错误。  
  116.                 throw new ClassNotFoundException(ie.toString());  
  117.             }  
  118.         }  
  119.         // 确保已经正确编译或者不需要编译,我们开始加载原始字节。  
  120.         try {  
  121.             // 读取字节。  
  122.             byte raw[] = getBytes(classFilename);  
  123.             // 转化为类对象  
  124.             clas = defineClass(name, raw, 0, raw.length);  
  125.             System.out.println("load class:"+classFilename+" classloader is:"+this);  
  126.         } catch (IOException ie) {  
  127.             // 这里并不表示失败,可能我们处理的类在本地类库中,如java.lang.Object。  
  128.         }  
  129.         // System.out.println( "defineClass: "+clas );  
  130.         // 可能在类库中,以默认的方式加载。  
  131.         if (clas == null) {  
  132.             clas = findSystemClass(name);  
  133.             //System.out.println("use define class:"+name);  
  134.         }  
  135.         // System.out.println( "findSystemClass: "+clas );  
  136.         // 如果参数resolve为true,根据需要解释类。  
  137.         if (resolve && clas != null)  
  138.             resolveClass(clas);  
  139.         // 如果还没有获得类,说明出错了。  
  140.         if (clas == null)  
  141.             throw new ClassNotFoundException(name);  
  142.         // 否则,返回这个类对象。  
  143.         return clas;  
  144.     }  
  145. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值