你如何动态编译和加载外部java类? [英] How do you dynamically compile and load external java classes?

查看:35
本文介绍了你如何动态编译和加载外部java类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(这个问题与我见过的许多问题类似,但大多数都不够具体,我正在做的)

(This question is similar to many questions I have seen but most are not specific enough for what I am doing)

背景:

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

The purpose of my program is to make it easy for people who use my program to make custom "plugins" so to speak, then compile and load them into the program for use (vs having an incomplete, slow parser implemented in my program). My program allows users to input code into a predefined class extending a compiled class packaged with my program. They input the code into text panes then my program copies the code into the methods being overridden. It then saves this as a .java file (nearly) ready for the compiler. The program runs javac (java compiler) with the saved .java file as its input.

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

My question is, how do I get it so that the client can (using my compiled program) save this java file (which extends my InterfaceExample) anywhere on their computer, have my program compile it (without saying "cannot find symbol: InterfaceExample") then load it and call the doSomething() method?

我一直看到使用反射或类加载器的问答以及几乎描述了如何编译它的问答,但对我来说都不够详细/我不完全理解它们.

I keep seeing Q&A's using reflection or ClassLoader and one that almost described how to compile it, but none are detailed enough for me/I do not understand them completely.

推荐答案

看看 JavaCompiler

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

The following is based on the example given in the JavaDocs

这将在testcompile目录中保存一个File(基于package名称要求)并编译File 到 Java 类...

This will save a File in the testcompile directory (based on the package name requirements) and the compile the File to a Java class...

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;
");
        sb.append("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff {
");
        sb.append("    public void doStuff() {
");
        sb.append("        System.out.println("Hello world");
");
        sb.append("    }
");
        sb.append("}
");

        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();
    }

}

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

Now updated to include suppling a classpath for the compiler and loading and execution of the compiled class!

这篇关于你如何动态编译和加载外部java类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆