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


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

    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);
                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 的实例才会释放。
    我在代码里加入了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 错误。

    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;
    	}
    }
    






  • 相关阅读:
    手脱ASPack v2.12变形壳2
    手脱nSPack 2.1
    WCF分布式开发步步为赢(1):WCF分布式框架基础概念
    一个经典例子让你彻彻底底理解java回调机制
    C#三层架构详细解剖
    eclipse快捷键及各种设置
    设计模式总结
    程序猿也爱学英语(上)
    关于PDA、GPS等动态资源的几种GIS解决方案
    通过VS2010性能分析来查找代码中那些地方最损耗资源
  • 原文地址:https://www.cnblogs.com/cl1024cl/p/6204793.html
Copyright © 2020-2023  润新知