我知道这个问题已经被问到并回答了很多,但我仍然没有一个好的解决方案,我仍然不明白一些部分。所以我需要以编程方式编译*.java文件。
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
是我正在使用的,并且(如预期的那样)编译器是null
。现在,我确实知道我必须使用JDK而不是JRE作为"运行时",但这里有一些我不理解的东西:仅仅将tools.jar
放在应用程序的类路径中,然后访问Java编译器API还不够吗?如果这是真的,那么独立的java应用程序和基于web的应用程序之间是否存在(我认为存在)区别?实际上,我正试图从PlayFramework web应用程序召唤javaccompiler,所以我弄清楚了,也许这个解决方案(包括tools.jar
)只适用于独立应用程序?
我还尝试创建一个自定义ClassLoader并调用上面提到的反射方法,但我所得到的是compiler
对象的null
:
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文档)我很确定在播放能够加载Java编译器类之前还有别的事情我需要做,我只是不知道我错过了什么。
我知道Runtime.exec("javac myFile.java")
和Eclipse JDT编译器这样的选项,但这不是我想要的。
哦,像System.setProperty("java.home", "PATH_TO_YOUR_JDK");
,然后ToolProvider.getSystemJavaCompiler();
是工作的,但我发现这个解决方案太丑了。
EDIT:(给出附加信息并反映最后状态)这是web-app-structure的基本表示:
myApp
|-conf...
|-libMyJar.jar
|-libtools.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时得到编译器!
web应用程序和独立应用程序之间没有区别。
你不应该把tools.jar打包到你的webapp类路径中。Java中的类装入器通常只能自下而上地工作。因此,作为jdk一部分的工具提供程序不会在webapp类路径中看到你的tools.jar(它不能向下查看webapp类路径)。
解决方案:使用JDK并确保指向它,或者将tools.jar放在Java ext目录中。您可以通过设置属性-Djava.ext来覆盖ext目录。
它在文档中说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);
where defaultJavaCompilerName = "com.sun.tools.javac.api.JavacTool"
将其子类化为JavaCompiler
并获得新的实例-您将拥有您的编译器