嗨,我正在尝试动态编译 java 代码(其中包含来自第三方 jar 的类,即依赖项)并执行相同的操作,但它对我不起作用。
例如:如果我想执行一个简单的java代码(System.out.println),它工作正常,但如果我添加任何第三方jar文件(例如:selenium)。它会抛出找不到符号的错误。
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class InlineCompiler {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder(64);
sb.append("package testcompile;\n");
sb.append("import org.sikuli.script.*;\n");
sb.append("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff {\n");
sb.append(" public void doStuff() {\n");
sb.append("Screen s = new Screen();\n");
sb.append(" System.out.println(\"Hello world\");\n");
sb.append(" }\n");
sb.append("}\n");
File helloWorldJava = new File("testcompile/HelloWorld.java");
if (helloWorldJava.getParentFile().exists() || helloWorldJava.getParentFile().mkdirs()) {
try {
Writer writer = null;
try {
writer = new FileWriter(helloWorldJava);
writer.write(sb.toString());
writer.flush();
} finally {
try {
writer.close();
} catch (Exception e) {
}
}
/** Compilation Requirements *********************************************************************************************/
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
// This sets up the class path that the compiler will use.
// I've added the .jar file that contains the DoStuff interface within in it...
List<String> optionList = new ArrayList<String>();
optionList.add("-classpath");
optionList.add(System.getProperty("java.class.path") + ";sikuli.jar");
Iterable<? extends JavaFileObject> compilationUnit
= fileManager.getJavaFileObjectsFromFiles(Arrays.asList(helloWorldJava));
JavaCompiler.CompilationTask task = compiler.getTask(
null,
fileManager,
diagnostics,
optionList,
null,
compilationUnit);
/********************************************************************************************* Compilation Requirements **/
if (task.call()) {
/** Load and execute *************************************************************************************************/
System.out.println("Yipe");
// Create a new custom class loader, pointing to the directory that contains the compiled
// classes, this should point to the top of the package structure!
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("./").toURI().toURL()});
// Load the class from the classloader by name....
Class<?> loadedClass = classLoader.loadClass("testcompile.HelloWorld");
// Create a new instance...
Object obj = loadedClass.newInstance();
// Santity check
if (obj instanceof DoStuff) {
// Cast to the DoStuff interface
DoStuff stuffToDo = (DoStuff)obj;
// Run it baby
stuffToDo.doStuff();
}
/************************************************************************************************* Load and execute **/
} else {
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
System.out.format("Error on line %d in %s%n",
diagnostic.getLineNumber(),
diagnostic.getSource().toUri());
}
}
fileManager.close();
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) {
exp.printStackTrace();
}
}
}
public static interface DoStuff {
public void doStuff();
}
}
代码应该运行,但可能会出现错误符号未找到。
最佳答案
首先,我建议在打印诊断信息时添加消息。 将打印错误消息的部分更改为:
System.out.format("Error %s on line %d in %s%n",
diagnostic.getMessage(null), // ⇐ Message, very useful
diagnostic.getLineNumber(),
diagnostic.getSource().toUri());
这将有助于理解编译器认为出了什么问题。
在 Linux 或 Mac OS 上,我们现在看到一个问题:即使 sikuli.jar
在正确的位置包含 Screen.class
,也找不到该类 Screen
。为什么?因为 Java 并不寻找 sikuli.jar
文件。为什么?因为使用了错误的路径连接字符。路径连接字符在 Windows 上为 ;
,在 POSIX-y 系统上为 :
。我们应该使用 System.getProperty("path.separator"),而不是硬编码 ;
。
我现在可以看到 3 个编译器错误(还有一条消息显示“错误”,但实际上是警告):
- 找不到符号“class DoStuff”
- 找不到符号“class Screen”(两次)
因此,将 optionList
中构建类路径条目的行更改为:
optionList.add(System.getProperty("java.class.path") + System.getProperty("path.separator") + "sikuli.jar");
进行此更改后,代码可以正常工作。
这是我使用的目录结构:
playground/ ⇐ current working directory
+- inlinecompiler/
| +- Compiler.java
+- sikuli.jar ⇐ contains org/sikuli/script/Screen.class
命令:
javac inlinecompiler/Compiler.java
java -cp .:sikuli.jar inlinecompiler.Compiler
这意味着如果它不起作用,还要检查您的目录结构。
最后但并非最不重要的一点是,java.lang.Compiler
已弃用并计划删除。您可能想切换到javax.tools.JavaCompiler
。您可以使用以下方式获取实例:
JavaCompiler compiler = ServiceLoader.load(JavaCompiler.class).iterator().next();
关于java - 如何以编程方式运行 Java 编译器时添加依赖项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54183528/