【问题标题】:Compile java files programmatically以编程方式编译 java 文件
【发布时间】:2013-03-31 13:53:23
【问题描述】:

我知道这个问题已经被问了很多,但我仍然没有一个好的解决方案,我仍然不明白某些部分。所以我需要以编程方式编译 *.java 文件。

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

是我正在使用的,并且(正如预期的那样)编译器是null。 现在,我确实知道我必须使用 JDK 而不是 JRE 作为“运行时”,但这里有一些我不明白的地方:仅仅将 tools.jar 放在应用程序的类路径中还不够吗?那么可以访问 Java Compiler API 吗?如果这是真的,那么独立的 Java 应用程序和基于 Web 的应用程序之间是否存在(我认为存在)差异。 实际上,我正在尝试从 PlayFramework webapp 调用 JavaCompiler,所以我发现,也许这个解决方案(包括 tools.jar)仅适用于独立应用程序?

我还尝试创建一个自定义 ClassLoader 并使用反射调用上述方法,但我得到的只是 null 用于 compiler 对象:

ClassLoader classloader = app.classloader();
File file = new File("lib/tools.jar");          
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader newCL = new URLClassLoader(urls, classloader) {
};
Class<?> loadClass = newCL.loadClass("javax.tools.ToolProvider");
Method method = loadClass.getMethod("getSystemJavaCompiler", null);             
Object object = method.invoke(null);
System.out.println("Object: " + object); // NULL

上面代码的解释:

  • 为了简单起见,我没有包含 try/catch。
  • app.classloader() 是一个 Play-method,它返回 App 的 ClassLoader
  • tools.jar 包含在 Play 项目的我的 lib 文件夹中(这意味着它位于项目的类路径中 - 根据 Play 文档)

我很确定在 Play 能够加载 Java Compiler 类之前我还需要做一些其他事情,我只是不知道我缺少什么。

Runtime.exec("javac myFile.java") 和 Eclipse JDT 编译器之类的选项我都知道,但这不是我想要的。

哦,像 System.setProperty("java.home", "PATH_TO_YOUR_JDK");ToolProvider.getSystemJavaCompiler(); 这样的东西正在工作,但我觉得这个解决方案太丑了。

最好的问候

编辑:(提供附加信息并反映最后状态) 这是 web-app-structure 的基本表示:

myApp
|-conf\...
|-lib\MyJar.jar
|-lib\tools.jar
|-logs\...
|-...

MyJar.jar 现在有一个 META-INF/MANIFEST.MF 文件,其内容如下:

Manifest-Version: 1.0
Sealed: true
Main-Class: here.comes.my.main.class
Class-Path: tools.jar

没有启动 Play 应用程序,我正在尝试(在 lib 文件夹中):java -jar MyJar.jar - 我的简单 main 方法尝试调用编译器 (ToolProvider.getSystemJavaCompiler();) 并返回null。所以这让我相信,这个问题与 Play 无关——在正常运行我的 Jar 时,我什至无法获得编译器!

【问题讨论】:

    标签: java compiler-construction playframework playframework-2.0 tools.jar


    【解决方案1】:

    Web 应用程序和独立应用程序之间没有区别。

    您不应该将 tools.jar 打包到您的 webapp 类路径中。 Java 中的类加载器通常只能自下而上工作。因此,作为 jdk 一部分的 ToolProvider 不会在 webapp 类路径中看到您的 tools.jar(它不能向下查看 webapp 类路径)。

    解决方案:使用 JDK 并确保指向它或将 tools.jar 放在 Java ext 目录中。您可以通过将属性 -Djava.ext.dir 设置为您喜欢的任何目录来覆盖 ext 目录。

    【讨论】:

    • 这听起来不错。我建议将工具 jar 放在一个公共的地方或使用 jdk。
    【解决方案2】:

    它在documentation 中说To run the Play framework, you need JDK 6 or later. 你是如何设法用jre 运行它的?

    无论如何,如果 getSystemJavaCompiler 返回 null 则意味着您的类路径中没有 tools.jar 或者它已损坏。如果是 Sun/Oracle java,请确保其中包含 com.sun.tools.javac.api.JavacTool 类。

    如果你想使用自定义类加载器,你需要加载的是 JavacTool 类,而不是 ToolProvider。看看它在 java 6 中的做法:

            URL[] urls = {file.toURI().toURL()};
            ClassLoader cl = URLClassLoader.newInstance(urls);
            cl.setPackageAssertionStatus("com.sun.tools.javac", true);
            return Class.forName(defaultJavaCompilerName, false, cl);
    

    在哪里defaultJavaCompilerName = "com.sun.tools.javac.api.JavacTool"

    将其子类化为 JavaCompiler 并获取新实例 - 你将拥有你的编译器。

    【讨论】:

    • JAVA_HOME 在我的开发机器上设置,在被要求时返回 JDK 的路径。但是从正在运行的 Play 应用程序(使用 System.getProperty(...))中获取 "java.home" 属性会返回 JRE 的路径。所以也许这是一个问题。但是当我的类路径中有tools.jar 时应该没有问题。而且我很确定 tools.jar 在我的类路径中(在运行 Web 应用程序期间检查过它)并且文件没有损坏。我将尝试上面提到的类加载的东西。
    • 我在我的 ubuntu 上运行了 play,我可以从 scala 操作中获取 java 编译器而没有任何问题。也许您的设置有问题?
    • 刚刚从app/Global.java 中的onStart 方法中尝试了JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); System.out.println("Java compiler: " + javaCompiler); - 在我的应用程序的类路径中使用tools.jar - 即使返回null
    猜你喜欢
    • 2015-03-09
    • 1970-01-01
    • 2014-06-06
    • 1970-01-01
    • 2016-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多