从字符串编译Java源代码?

10

有没有一种方法让正在运行的Java程序编译Java源代码(作为字符串传递)?

Class newClass = Compiler.compile ("class ABC { void xyz {etc. etc. } }");
理想情况下,程序的类加载器应该能够解析传入源代码所引用的任何类。是否存在此类功能?

我找到了这个例子,正是我所需要的。http://www.ibm.com/developerworks/java/library/j-jcomp/index.html - psksvp
5个回答

10
当然。看一下JavaCompiler类和javax.tools包中的其他类。
它们自Java 1.6以来就存在了。 这里有一些示例代码。 (正如评论中@Sergey Tachenov指出的那样,它需要安装JDK,因为必要的tools.jar文件随JDK而来,而不是JRE。)

4
值得注意的是,它不仅可以与Sun的JRE一起使用,还需要安装JDK,因为必要的tools.jar文件随JDK而来,而不是JRE。 - Sergei Tachenov
我尝试了这个例子,但在Class<?> clazz = Class.forName("Test");处遇到了java.lang.ClassNotFoundException: Test的问题! - bachr
编译的输出目录是否在类路径上? - aioobe

4
你需要的是一个扩展了JavaFileObject类的类。
import java.net.URI;

import javax.tools.SimpleJavaFileObject;

public class JavaSourceFromString extends SimpleJavaFileObject {
    final String code;

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

以下是使用方法:

可以按如下方式使用:

JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
if( jc == null) throw new Exception( "Compiler unavailable");

String code = "public class CustomProcessor { /*custom stuff*/ }";
JavaSourceFromString jsfs = new JavaSourceFromString( "CustomProcessor", code);

Iterable<? extends JavaFileObject> fileObjects = Arrays.asList( jsfs);

List<String> options = new ArrayList<String>();
options.add("-d");
options.add( compilationPath);
options.add( "-classpath");
URLClassLoader urlClassLoader =
         (URLClassLoader)Thread.currentThread().getContextClassLoader();
StringBuilder sb = new StringBuilder();
for (URL url : urlClassLoader.getURLs()) {
    sb.append(url.getFile()).append(File.pathSeparator);
}
sb.append( compilationPath);
options.add(sb.toString());

StringWriter output = new StringWriter();
boolean success = jc.getTask( output, null, null, options, null, fileObjects).call(); 
if( success) {
    logger.info( LOG_PREFIX + "Class has been successfully compiled");
} else {
    throw new Exception( "Compilation failed :" + output);
}

你在哪里声明了 compilationPath 变量? - bachr
它在代码中更高的位置,只是一个字符串而已。 - MonoThreaded

2

这取决于你想做什么。 如果你只是想运行一些代码,可以使用BeanShell。它不是一个Java编译类,但非常有用,可以使某些事情更加灵活。


1
你可以尝试使用我的 essence jcf 库来实现这个功能。在调试模式下,你可以将源代码写入文件中,以便你可以逐步进入代码。否则,它会在内存中完成所有操作。它将 JavaCompiler 包装在 tools.jar 中。
它接受一个字符串,编译并加载到当前类加载器中,并返回该类。它还处理嵌套/内部类。

http://vanillajava.blogspot.com/2010/11/more-uses-for-dynamic-code-in-java.html

注意:我还没有在OSGi中使其工作。;)

1
Javassist可以从源代码字符串生成和加载类和方法,也可以在需要时将生成的类转储到文件系统中。
目前,在这些字符串中传递的代码存在一些小限制,例如它不能包括泛型、枚举或基本类型的自动装箱和拆箱。
更多信息请参见:

http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接