针对 Java 5 和 Java 6 的动态内存中 Java 代码编译

如何在Java 5和Java 6中从任意字符串(内存中)编译Java代码,加载它并在其上运行特定方法(预定义)?

在你讨论这个问题之前,我看了一下现有的实现:

  • 大多数依赖于Java 6 Compiler API。
  • 那些不这样做的人,依靠技巧。
  • 是的,我查看了commons-jci。要么我太密集而无法理解它是如何工作的,要么就是它不理解。
  • 我找不到如何为编译器提供我当前的类路径(这是相当大的)。
  • 在有效的实现(在Java 6中),我找不到如何正确加载内部类(或内部匿名类)。
  • 如果整个事情都在内存中,我会非常喜欢它,因为事情在多个环境中运行。

我确信这个问题以前已经解决了,但我在谷歌上找不到任何看起来甚至一半生产质量的东西(除了jci,正如我之前所说,我还没有设法使用)。

编辑:

  • 我查看了JavaAssist - 我需要内部类,Java 5.0语言级别的支持以及使用整个类路径进行编译。另外,我想在飞行中创建新类。我可能弄错了,但我找不到如何使用JavaAssit做到这一点。
  • 我愿意使用基于文件系统的解决方案(调用javac),但我不知道如何划分类路径,也不知道如何稍后使用特殊的类加载器加载文件(不在我的类路径中),该类加载器可以回收用于多次调用。虽然我知道如何研究它,但我更喜欢现成的解决方案。

Edit2:目前,我对BeanShell“评估”感到满意。显然,它做了我需要的一切(获取一个字符串,在“当前”类路径的上下文中评估它。它确实缺少Java 5的一些功能,但它可以使用枚举(不定义)和编译的“泛型”(擦除)类,所以它应该足以满足我的需求。

我不想将答案标记为已接受,因为我确实希望出现更好的解决方案。

编辑3:接受了beanshell的建议 - 它确实很棒。


答案 1

JCI看起来很好。这个代码片段应该是你的基础:

JavaCompiler compiler = new JavaCompilerFactory().createCompiler("eclipse");

MemoryResourceReader mrr = new MemoryResourceReader();
mrr.add("resource name string", yourJavaSourceString.getBytes());

MemoryResourceStore mrs = new MemoryResourceStore();

CompilationResult result = compiler.compile(sources, mrr, mrs);

// don't need the result, unless you care for errors/warnings
// the class should have been compiled to your destination dir

这有什么原因不应该起作用吗?


编辑:添加了一个将编译好的类输出发送到内存,就像请求的那样。MemoryResourceStore

此外,设置设置,如您情况下的类路径,可以通过类中的setCustomArguments(String[] pCustomArguments)来完成。javacJavacJavaCompilerSettings


答案 2

您可能还想看看Janino。

从他们的网站:

Janino 是一个编译器,它读取 JavaTM 表达式、块、类体、源文件或一组源文件,并生成直接加载和执行的 JavaTM 字节码。Janino不是一个开发工具,而是一个用于运行时编译目的的嵌入式编译器,例如表达式计算器或JSP等“服务器页面”引擎。

http://www.janino.net/

我目前在一个相当大的关键任务项目中使用它,它工作得很好


推荐