【问题标题】:On-the-fly, in-memory java code compilation for Java 5 and Java 6Java 5 和 Java 6 的动态内存中 Java 代码编译
【发布时间】:2010-10-11 14:52:01
【问题描述】:

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

在你发火之前,我查看了现有的实现:

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

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

编辑:

  • 我查看了 JavaAssist - 我需要内部类、Java 5.0 语言级别支持以及使用整个类路径进行编译。另外,我想即时创建新课程。一世 可能是错误的,但我找不到如何使用 JavaAssit 执行此操作。
  • 我愿意使用基于文件系统的解决方案(调用 javac),但我不知道如何预测类路径,也不知道以后如何使用特殊的类加载器加载文件(不在我的类路径中)可以为多次调用而回收。虽然我知道如何研究它,但我更喜欢现成的解决方案。

编辑2: 目前,我对 BeanShell 的“评估”感到满意。显然它做了我需要它做的一切(获取一个字符串,在“当前”类路径的上下文中评估它。它确实错过了一些 Java 5 功能,但它可以使用枚举(未定义)和编译的“通用”(删除) 类,所以对于我想要的应该足够了。

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

Edit3:接受了 beanshell 的建议 - 它真的很有效。

【问题讨论】:

    标签: java compiler-construction runtime compilation runtime-compilation


    【解决方案1】:

    Javassist你可能会感兴趣

    【讨论】:

    • 我查看了 JavaAssist,但它并不是我想要的。我想运行以字符串形式提供给我的代码,这些代码依赖于我的类 - 我找不到使用 JavaAssist 执行此操作的方法。此外,JavaAssist 的局限性(没有 5.0 功能)对我来说非常有限。
    【解决方案2】:

    JCI 看起来不错。这段代码 sn-p 应该是你的基础:

    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 将编译后的类输出发送到内存,就像请求的那样。

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

    【讨论】:

    • 没有理由,但是: 1. 我不知道如何预测类路径。正如我之前所说,它庞大且相当复杂。我希望编译器使用“内存中”的类进行编译。 2. 我将使用文件输出作为最后的手段,但我想先探索内存中的解决方案。
    【解决方案3】:

    如果你不完全依赖于编译,像 Beanshell、groovy 和其他脚本语言这样的解决方案很容易嵌入(事实上,java 内置了对插入脚本语言的支持,所以你的代码甚至不需要知道脚本是用什么语言编写的)

    Beanshell 应该运行任何 100% java 代码 IIRC,我相信 Groovy 可以运行大多数 java 代码——可能全部。

    【讨论】:

    • 这是一个很好的解决方案,但我仍然希望有一个更好的具有真正编译过程的解决方案。如果没有其他问题,我会将其标记为已接受。谢谢。
    • 你知道,我认为这个 API 一直可用:openjdk.java.net/groups/compiler/doc/package-overview/… 但它不在 java 中。因此可能会发生变化(尽管我认为变化不大)
    • 我并不是说这对每个人来说都是正确的解决方案,但它完美地满足了我的需求。 BSH 2.0 真的可以创造奇迹。
    【解决方案4】:

    你可能也想看看 Janino。

    来自他们的网站:

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

    http://www.janino.net/

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

    【讨论】:

    • 忘了说。我确实检查了 Janino。正如我所说,我需要 5.0 功能才能与现有代码互操作。
    • Janino 支持一些 5.0 功能,我只是不确定它是否能涵盖您需要的功能。祝你好运;我很想听听你的决定。
    • Janino 似乎远远优于 BeanShell。 1.5 的功能太糟糕了。
    【解决方案5】:

    在像 Tomcat 这样的 web 容器中运行,首先生成一个 JSP 页面,然后调用它。

    这还允许您通过简单地覆盖 JSP 页面而不是让您的类加载器缓慢运行满来摆脱旧的类定义。

    “内存”要求是由于速度还是由于不更改代码库?

    【讨论】:

    • 其实到现在我都用过那个方法。它很笨重,但它有效。但是,我讨厌它,客户不信任它。我宁愿使用更干净(或更好隐藏)的方法。
    • 我敢打赌你会更讨厌类加载器问题 ;-) 做事要非常小心 :)
    【解决方案6】:

    ECJ Eclipse Java Compiler

    Eclipse 提供并使用自己的编译器,不是 javac

    • Eclipse 编译器在 IDE (Eclipse) 内部使用
    • Eclipse 编译器也可以用作纯批处理编译器 在 Eclipse 之外

    编译源文件

    $ java -jar ecj-3.5.2.jar HelloWorld.java

    【讨论】:

      猜你喜欢
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多