以编程方式编译Java文件

10

我知道这个问题已经被问了很多次并得到了答案,但我仍然没有一个好的解决方案,也不理解其中的一些部分。因此,我的要求是以编程方式编译*.java文件。

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

我使用的是JRE,而(如预期)编译器为null。 我知道必须使用JDK而不是作为“运行时”的JRE,但有些事情我不明白:只需将tools.jar放入应用程序的类路径中,就可以访问Java Compiler API了吗? 如果是这样,那么独立的Java应用程序和基于Web的应用程序之间是否有区别(我认为有)。

实际上,我正在尝试从PlayFramework Web应用程序中调用JavaCompiler,因此我想,也许这种解决方案(包括tools.jar)仅适用于独立应用程序?

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

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方法,返回应用程序的ClassLoader
  • tools.jar 已包含在Play项目的lib文件夹中(这意味着根据Play文档,它在项目的类路径上)。

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

Runtime.exec("javac myFile.java") 和 Eclipse JDT编译器等选项对我来说都很熟悉,但这不是我在寻找的。

哦,还有类似于 System.setProperty("java.home", "PATH_TO_YOUR_JDK"); 然后 ToolProvider.getSystemJavaCompiler(); 这样的解决方案可以工作,但我认为这个解决方案太丑陋了。

最好的祝福

编辑:(提供额外信息并反映最新状态) 这是Web应用程序结构的基本表示:

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也无法获得编译器!

2个回答

1

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

您不应该将tools.jar打包到您的Web应用程序类路径中。在Java中,类加载器通常只能从下往上工作。因此,jdk中的ToolProvider将无法看到您Web应用程序类路径中的tools.jar(它不能向下查看Web应用程序类路径)。

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


这听起来不错。我建议将工具jar放在一个公共位置或使用JDK。 - tgkprog

0

文档中说:要运行Play框架,您需要JDK 6或更高版本。你是如何使用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应用程序中获取“java.home”属性(使用System.getProperty(...))将返回JRE的路径。所以可能这就是问题所在。但是当我在类路径中有tools.jar时,就不应该有问题。而且我很确定tools.jar在我的类路径中(在Web应用程序运行期间进行了检查),并且该文件没有损坏。我将尝试上述提到的类加载内容。 - user2229298
我在我的Ubuntu上运行play,并且可以从Scala操作中获取Java编译器,没有任何问题。也许是你的设置有问题? - Denis Tulskiy
刚刚尝试了从app/Global.javaonStart方法中使用JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); System.out.println("Java compiler: " + javaCompiler); - 在我的应用程序类路径中包含tools.jar - 但是它仍然返回null - user2229298

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