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);
}
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){
}
}
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();
}
}
}
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;
- }
- }