Java反射,如何在运行时重新加载已更改和重新编译的类?

时间:2013-07-17 02:43:35

标签: java reflection classloader dynamic-class-loaders

我正在开发类似IDE的项目,其中由用户更改的代码在运行时由JavaCompiler重新编译,需要重新加载以执行更改的代码,我使用反射来执行此操作,但问题是类曾经加载过ClassLoader在重新执行下面的代码时永远不会被更改它保持静态,但是当我退出整个应用程序并重新启动它时,我可以看到重新编译代码中的更改。以下是我正在使用的代码:

    Class<?> clazz = Class.forName("Projects.Demo."+classname);
    Constructor<?> ctor = clazz.getConstructor(App.class, Ctrl.class);
    Object object = ctor.newInstance(new Object[] { app , ctrl}); 

我找到的解决方案之一是在java2s.com上,标题为“动态重新加载已修改的类”:

    import java.io.File;
    import java.net.URL;
    import java.net.URLClassLoader;

    class MyClass{
      public String myMethod() {
           return "a message";
      }
    }

    public class Main {
      public static void main(String[] argv) throws Exception {
        URL[] urls = null;
        File dir = new File(System.getProperty("user.dir") + File.separator + "dir"         +     File.separator);
        URL url = dir.toURI().toURL();
        urls = new URL[] { url };
        ClassLoader cl = new URLClassLoader(urls);
        Class cls = cl.loadClass("MyClass");
        MyClass myObj = (MyClass) cls.newInstance();

      }

但它不适合我,因为更改的类永远不会被此代码重新加载。

如果有其他选择可以帮助我或建议我。

1 个答案:

答案 0 :(得分:3)

好的,这是我必须工作的东西。

如果Myclass位于标准类路径中,请注意它不起作用。

package nz.test.loader;

public interface Executer {

    public void execute();

}


package nz.test.loader;

import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;

public class LoadClass {

    public static void main(String[] argv) throws Exception {

        URL[] urls = null;

        File dir = new File(System.getProperty("user.dir") + File.separator + "out/dir" + File.separator);
        File classFile = new File(dir,"nz/co.test/loader/MyClass.class");
        long lastModified = classFile.lastModified();
        URL url = dir.toURI().toURL();
        urls = new URL[] { url };
        ClassLoader cl = new URLClassLoader(urls);
        compileClass("first class", dir.getAbsolutePath());

        Class cls = cl.loadClass("nz.test.loader.MyClass");
        Executer myObj = (Executer) cls.newInstance();

        myObj.execute();
        compileClass("another class", dir.getAbsolutePath());
        cl = new URLClassLoader(urls);

        cls = cl.loadClass("nz.test.loader.MyClass");
        myObj = (Executer) cls.newInstance();

        myObj.execute();

    }

    public static void compileClass(String message, String destination) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

        StringWriter writer = new StringWriter();
        PrintWriter out = new PrintWriter(writer);
        out.println("package nz.test.loader;");
        out.println("public class MyClass implements Executer{");
        out.println("  public void execute() {");
        out.println("    System.out.println(\""+message+"\");");
        out.println("  }");
        out.println("}");
        out.close();
        JavaFileObject file = new JavaSourceFromString("nz.test.loader.MyClass", writer.toString());

        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);

        List<String> optionList = new ArrayList<String>();

        JavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null,null);
        List<String> params = new ArrayList();
        params.add(destination);
        fileManager.handleOption("-d",params.iterator());
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, optionList, null, compilationUnits);

        boolean success = task.call();
        for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
            System.out.println(diagnostic.getCode());
            System.out.println(diagnostic.getKind());
            System.out.println(diagnostic.getPosition());
            System.out.println(diagnostic.getStartPosition());
            System.out.println(diagnostic.getEndPosition());
            System.out.println(diagnostic.getSource());
            System.out.println(diagnostic.getMessage(null));

        }
        System.out.println("Success: " + success);

    }

}

class JavaSourceFromString extends SimpleJavaFileObject {
    final String code;

    JavaSourceFromString(String name, String code) {
        super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension),Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}