我正在开发一个用户可以在原始Java代码中存储插件的项目。然后我的应用程序将获取这些插件,编译它们并导入它们。这些类基于存储在我的jar中的接口。但是,当我尝试使用JavaCompiler.CompilationTask运行它时,它拒绝允许我将当前jar添加到编译器的类路径中。在这种情况下,当它试图编译它时,就好像接口不可用于实现一样。
以下是我的文件结构:
主.jar文件:
CommandProcessor.java
----------------------------------------------
package plugins;
public interface CommandProcessor {
public String onCommand(String command);
}
然后我有一个加载插件的功能。
http://hastebin.com/jabacopeye.coffee(HasteBin避免过度混乱的问题)
以下是其中一个用户插件的示例:
public class MyCommand implements plugins.CommandProcessor {
@Override
public String onCommand(String command){
return "this is a test";
}
}
每当应用程序尝试编译这个外部存储的.java文件时,它都会说“plugins.CommandProcessor”类不存在。
答案 0 :(得分:3)
如你所述,
每当应用程序尝试编译此外部存储的.java时 文件,它说类" plugins.CommandProcessor"才不是 存在。
task.call()
时,URLClassLoader无法从"./plugins/WebC/plugins/"
位置加载文件。 因此,您的代码无法继续执行
if (task.call()) {
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("./plugins/WebC/plugins/").toURI().toURL()});
Class<?> loadedClass = classLoader.loadClass(className);
Object obj = loadedClass.newInstance();
if (obj instanceof CommandProcessor) {
CommandProcessor cmd = (CommandProcessor)obj;
classLoader.close();
return cmd;
}else{
classLoader.close();
}
}
所以你需要在类路径上指定jar。
java编译器和运行时不仅可以在单独的文件中搜索类,还可以在JAR' archives. A JAR file can maintain its own directory structure, and Java follows exactly the same rules as for searching in ordinary directories. Specifically,
目录名=包名&#39;中搜索类。由于JAR本身就是一个目录,要在类搜索路径中包含JAR文件,该路径必须引用JAR本身,而不仅仅是包含JAR的目录。这是一个非常常见的错误。假设我在目录/ myclasses中有一个JAR myclasses.jar。要让Java编译器在这个jar中查找类,我们需要指定:
javac -classpath /myclasses/myclasses.jar ...
而不仅仅是目录myclasses。
在上面的例子中,我们告诉javac一次只搜索一个目录。实际上,您的类搜索路径将包含许多目录和JAR存档。 javac和java的-classpath选项允许指定多个条目,但请注意Unix和Windows系统的语法略有不同。 在Unix上,我们会这样做:
javac -classpath dir1:dir2:dir3 ...
而在Windows上我们有:
javac -classpath dir1;dir2;dir3 ...
区别的原因是Windows使用冒号(:)字符作为文件名的一部分,因此它不能用作文件名分隔符。当然,目录分隔符也是不同的:Unix的正斜杠(/)和Windows的反斜杠()。
答案 1 :(得分:1)
使用OpenHFT库 https://github.com/OpenHFT/Java-Runtime-Compiler您可以轻松解决问题。
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String className = "plugins.MyCommand";
String javaCode =
"package plugins;\n" +
"public class MyCommand implements plugins.CommandProcessor {"+
" @Override"+
" public String onCommand(String command){"+
" return \"this is a test\";"+
" }"+
"}";
Class aClass = CompilerUtils.CACHED_COMPILER
.loadFromJava(className, javaCode);
CommandProcessor processor = (CommandProcessor) aClass.newInstance();
System.out.println(processor.onCommand(""));
}
我运行了此代码并且(正如预期的那样)输出为"this is a test"
。您可以从文件中动态加载类。
如果选择此选项,则需要添加项目的依赖关系${env.JAVA_HOME}/lib/tools.jar
和OpenHFT
答案 2 :(得分:1)
您应该使用可以处理您期望的分辨率的自定义ClassLoader
来创建所有类。您可以编写任何外部jar文件内容的加载代码以及所需的接口。 JVM将跟踪您定义的任何类。
主程序将创建即将编写的ScriptingClassLoader
,其中包含解析脚本源代码:
public class ScriptingClassLoader extends ClassLoader {
public Class findClass(String name) {
// you can fixup the desired api interface here
// as you already know where they are
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// go find your script source and compile
// can be local disk, etc. You are only returning
// the byte code here
}
}
http://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html