在代码中编译,而不是获得我想要的行为。

时间:2011-12-19 18:39:20

标签: java dynamic compiler-construction dynamic-class-loaders

所以我有一些代码 - 我想编译字符串'temp'然后执行它,然后更改字符串,重新编译和执行。问题是,目前它只是首先执行代码的第一位。

我期待:

This is in another java file
How about now?

我得到了:

This is in another java file
This is in another java file

完整的代码如下,任何帮助表示赞赏。

import java.io.IOException;
import java.util.Arrays;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;

public class Another2 {

    public static void main(String args[]) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

        //First run
        String temp = "public class HelloWorld {\n" + "  public static void main(String args[]) {\n"
                + "    System.out.println(\"First Compiled Class\");\n" + "  }\n" + "}";

        JavaFileObject file = new JavaSourceFromString("HelloWorld", temp);
        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
        CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits);
        task.call();
        try {
            Class.forName("HelloWorld").getDeclaredMethod("main", new Class[] { String[].class })
                    .invoke(null, new Object[] { null });
        } catch (Exception e) {
            // handled it
        }

        //second run
        temp = "public class HelloWorld {\n" + "  public static void main(String args[]) {\n"
                + "    System.out.println(\"How About Now?\");\n" + "  }\n" + "}";
        file = new JavaSourceFromString("HelloWorld", temp);
        Iterable<? extends JavaFileObject> compilationUnits2 = Arrays.asList(file);
        task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits2);
        task.call();
        try {
            Class.forName("HelloWorld").getDeclaredMethod("main", new Class[] { String[].class })
                    .invoke(null, new Object[] { null });
        } catch (Exception e) {
            // handled it
        }
    }

}

2 个答案:

答案 0 :(得分:2)

我怀疑默认的类加载器正在缓存类文件而你的第二个Class.forName()实际上并没有做任何事情。您需要创建自己的类加载器并使用它。

答案 1 :(得分:0)

事实证明我正在寻找的代码是:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;

import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class AnotherClassLoaderRunner {

    private static final String nameOfDemoClass = "ByeWorld";

    public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        // Start compilation and loading of first class.
        String sourceCode = "public class " + nameOfDemoClass + " {\n" + "  public static void main(String args[]) {\n"
                + "    System.out.println(\"First of our Compiled Class\");\n" + "  }\n" + "}";
        AnotherClassLoader classLoader = new AnotherClassLoader(AnotherClassLoaderRunner.class.getClassLoader(), nameOfDemoClass);
        Class<?> myclass = classLoader.loadClass(nameOfDemoClass,sourceCode);
        Method mymethod = myclass.getDeclaredMethod("main", new Class[] { String[].class });
        mymethod.invoke(null, new Object[] { null });

        // Start compilation and loading of second class.
        sourceCode = sourceCode.replace("First of our Compiled Cla", "Second of our Compiled Cla");
        classLoader = new AnotherClassLoader(AnotherClassLoaderRunner.class.getClassLoader(), nameOfDemoClass);
        myclass = classLoader.loadClass(nameOfDemoClass,sourceCode);
        mymethod = myclass.getDeclaredMethod("main", new Class[] { String[].class });
        mymethod.invoke(null, new Object[] { null });
    }
}

class AnotherClassLoader extends ClassLoader {

    private static final String pathToClassDirectory = "file:/Users/*********/Documents/workspace/Dynamic/src/";
    private String targetName;

    public AnotherClassLoader(ClassLoader parent, String target) {
        super(parent);
        targetName = target;
    }

    public Class<?> loadClass(String name, String compileme) throws ClassNotFoundException {
        if (!targetName.equals(name)) {
            return super.loadClass(name);
        }
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaFileObject file = new JavaSourceFromString(name, compileme);
        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
        CompilationTask task = compiler.getTask(null, null, new DiagnosticCollector<JavaFileObject>(), null, null,
                compilationUnits);
        task.call();
        // THE CLASSFILE FOR THE SYSTEM EXSITS AT THIS POINT, thought how it
        // chooses the directory escapes me.
        try {
            String url = pathToClassDirectory + targetName + ".class";
            URL myUrl = new URL(url);
            URLConnection connection = myUrl.openConnection();
            InputStream input = connection.getInputStream();
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int data = input.read();
            while (data != -1) {
                buffer.write(data);
                data = input.read();
            }
            input.close();
            byte[] classData = buffer.toByteArray();

            return defineClass(targetName, classData, 0, classData.length);

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

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