您的位置:首页 > 编程语言

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

2014-08-15 20:06 393 查看
V1.1


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

[java] view
plaincopy

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,
4000
 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);  

            try{  

                Constructor c1=c.getDeclaredConstructor(new Class[]{String.class});   

                c1.setAccessible(true);     

                DemoInterface a1=(DemoInterface)c1.newInstance(new Object[]{"Demo"});  

            }catch(NoSuchMethodException e){  

                System.out.println("构造函数不存在");  

                e.printStackTrace();  

            }  

  

            DemoInterface instance=null;  

            try{  

                Constructor c0=clas.getDeclaredConstructor();  

                c0.setAccessible(true);     

                instance=(DemoInterface)c0.newInstance();    

            }catch(NoSuchMethodException e){  

                System.out.println("构造函数不存在");  

                e.printStackTrace();  

            }  

  

            ccl=null;//这里讲主动释放cc1  

            //DemoInterface instance=(DemoInterface)clas.newInstance();  

            if (instance!=null)  

            {  

                objList.add(instance);  

                instance.print("demo");  

                // 利用反射调用它的函数和传递参数。  

                // 产生一个代表主函数的参数类型的类对象。  

                Class mainArgType[] = { String.class };  

                //在类中找函数。  

                Method method = clas.getMethod( "print", mainArgType );  

                Object[] argsArray={"Demo"};  

                //调用方法。  

                method.invoke(instance, argsArray );                  

  

            }  

            if (ii>20)  

            {  

                ii=0;  

                objList.clear();  

            }  

            Thread.sleep(500);  

            //强制gc,只有objList清空后 CompilingClassLoader的实例才会释放。  

            //因为只有由CompilingClassLoader载入的class的实例全部释放后,CompilingClassLoader才能被释放  

            System.gc();  

  

        }  

    }  

}  

V1.0

在运行过程住动态编译并重新加载Class
需要继承ClassLoader ,没有不同的CompilingClassLoader


加载的class是不相同的不能相互转换。


对于CompilingClassLoader 的实例,只是简单的赋null是会自动释放的,只有由其载入的class的全部实例都释放后,


CompilingClassLoader 的实例才会释放。




我在代码里加入了[b]protected[/b] 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
plaincopy

package com.ailk;  

  

import java.io.*;  

  

/* 

 CompilingClassLoader动态的编译Java源文件。它检查.class文件是否存在,.class文件是 

 否比源文件陈旧。 

 */  

public class CompilingClassLoader extends ClassLoader {  

    protected void finalize()  

    {  

        System.out.println("finalize this:"+this);  

        try {  

            super.finalize();  

        } catch (Throwable e) {  

            // TODO Auto-generated catch block  

            e.printStackTrace();  

        }  

  

     }  

    // 指定一个文件名,从磁盘读取整个文件内容,返回字节数组。  

    private byte[] getBytes(String filename) throws IOException {  

        // 获得文件大小。  

        File file = new File(filename);  

        long len = file.length();  

        // 创建一个数组刚好可以存放文件的内容。  

        byte raw[] = new byte[(int) len];  

        // 打开文件  

        FileInputStream fin = new FileInputStream(file);  

        // 读取所有内容,如果没法读取,表示发生了一个错误。  

        int r = fin.read(raw);  

        if (r != len)  

            throw new IOException("Can't read all, " + r + " != " + len);  

        // 别忘了关闭文件。  

        fin.close();  

        // 返回这个数组。  

        return raw;  

    }  

  

    // 产生一个进程来编译指定的Java源文件,制定文件参数.如果编译成功返回true,否者,  

    // 返回false。  

    private boolean compile(String javaFile) throws IOException {  

        // 显示当前进度  

        System.out.println("CCL: Compiling " + javaFile + "...");  

        // 启动编译器  

        Process p = Runtime.getRuntime().exec(  

                "javac -classpath " + CompilingClassLoader.class.getResource("/").getPath()  

                        + " -Xlint:unchecked " + javaFile);  

        // 等待编译结束  

        try {  

            p.waitFor();  

        } catch (InterruptedException ie) {  

            System.out.println(ie);  

        }  

        // 检查返回码,看编译是否出错。  

        int ret = p.exitValue();  

        // 返回编译是否成功。  

        return ret == 0;  

    }  

  

    // 类加载器的核心代码 -加载类在需要的时候自动编译源文件。  

    public Class loadClass(String name, boolean resolve)  

            throws ClassNotFoundException {  

        // if (!name.startsWith("com.ailk.dynamic")){  

        // return getParent().loadClass(name);  

        // }  

        // 我们的目的是获得一个类对象。  

        Class clas = null;  

        // 首先,检查是否已经出理过这个类。  

        clas = findLoadedClass(name);  

        if (clas != null)  

            return clas;  

//      if (clas == null) {  

//          try {  

//              if (getParent() != null) {  

//                  clas = super.findClass(name);  

//              } else {  

//                  clas = findSystemClass(name);  

//              }  

//          } catch (ClassNotFoundException e) {  

//              // If still not found, then invoke findClass in order  

//              // to find the class.  

//              //clas = findClass(name);  

//          }  

//      }  

  

        // System.out.println( "findLoadedClass: "+clas );  

        // 通过类名获得路径名 比如:java.lang.Object => java/lang/Object  

        String fileStub = name.replace('.', '/');  

        // 构建指向源文件和类文件的对象。  

        String javaFilename = CompilingClassLoader.class.getResource("/").getPath() + "reload/"  

                + fileStub + ".java";  

        //System.out.println(javaFilename);  

        String classFilename = CompilingClassLoader.class.getResource("/").getPath() + "reload/"  

                + fileStub + ".class";  

        //System.out.println(classFilename);  

        File javaFile = new File(javaFilename);  

        File classFile = new File(classFilename);  

        // System.out.println( "j "+javaFile.lastModified()+" c "  

        // +classFile.lastModified() );  

        // 首先,判断是否需要编译。如果源文件存在而类文件不存在,或者都存在,但是源文件  

        // 较新,说明需要编译。  

        boolean javaExists = javaFile.exists();  

        boolean classExists = classFile.exists();  

        if (javaFile.exists()  

                && (!classFile.exists() || javaFile.lastModified() > classFile  

                        .lastModified())) {  

            try {  

                // 编译,如果编译失败,我们必须声明失败原因(仅仅使用陈旧的类是不够的)。  

                if (!compile(javaFilename) || !classFile.exists()) {  

                    throw new ClassNotFoundException("Compile failed: "  

                            + javaFilename);  

                }  

            } catch (IOException ie) {  

                // 可能编译时出现IO错误。  

                throw new ClassNotFoundException(ie.toString());  

            }  

        }  

        // 确保已经正确编译或者不需要编译,我们开始加载原始字节。  

        try {  

            // 读取字节。  

            byte raw[] = getBytes(classFilename);  

            // 转化为类对象  

            clas = defineClass(name, raw, 0, raw.length);  

            System.out.println("load class:"+classFilename+" classloader is:"+this);  

        } catch (IOException ie) {  

            // 这里并不表示失败,可能我们处理的类在本地类库中,如java.lang.Object。  

        }  

        // System.out.println( "defineClass: "+clas );  

        // 可能在类库中,以默认的方式加载。  

        if (clas == null) {  

            clas = findSystemClass(name);  

            //System.out.println("use define class:"+name);  

        }  

        // System.out.println( "findSystemClass: "+clas );  

        // 如果参数resolve为true,根据需要解释类。  

        if (resolve && clas != null)  

            resolveClass(clas);  

        // 如果还没有获得类,说明出错了。  

        if (clas == null)  

            throw new ClassNotFoundException(name);  

        // 否则,返回这个类对象。  

        return clas;  

    }  

}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: