java如何动态编译和加载外部Java类?

(此问题与我所见过的许多问题相似,但大多数问题对我的工作不够具体)

背景:

我的程序的目的是使使用我的程序的人可以轻松制作自定义的“插件”,然后将其编译并加载到程序中使用(与在我的程序中实现的不完整,缓慢的解析器相比)。我的程序允许用户将代码输入到预定义的类中,从而扩展了与我的程序一起打包的已编译类。他们将代码输入到文本窗格中,然后我的程序将代码复制到被覆盖的方法中。然后,将其保存为(几乎)准备供编译器使用的.java文件。该程序以保存的.java文件作为输入来运行javac(java编译器)。

我的问题是,如何获取它,以便客户端(使用我的编译程序)可以将此java文件(扩展了InterfaceExample)保存在他们的计算机上的任何位置,让我的程序对其进行编译(不说“找不到符号:InterfaceExample” ),然后加载它并调用doSomething()方法?

我一直看到Q&A使用反射或ClassLoader,并且几乎描述了如何编译它,但是对于我来说,没有一个足够详细的描述,我不完全理解它们。

回答:

以下内容基于JavaDocs中给出的示例

这会将a保存Filetestcompile目录中(基于package名称要求),然后将其编译File为Java类。

package inlinecompiler;

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("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff {\n");

sb.append(" public void doStuff() {\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") + File.pathSeparator + "dist/InlineCompiler.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();

}

}

现在已更新,包括为编译器提供类路径以及已编译类的加载和执行!

以上是 java如何动态编译和加载外部Java类? 的全部内容, 来源链接: utcz.com/qa/435289.html

回到顶部