在字符串中编译Java代码

3
我在字符串中编写了一个简单的 Java 代码,并希望在程序运行时动态执行,以下是我的代码:
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

public class CompliedClass {
    public static void main(String[] args) {
        String source = ""
                +"class Solution{"
                +"public int add(){"
                +"return 1+1;"
                +"}"
                +"}";

        //File root = new File("");
        File sourceFile = new File("./src/Solution.java");
        try {
            Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(sourceFile.getPath());
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null) {
            System.out.println("JDK required (running inside of JRE)");
        }else{
            System.out.println("you got it!");
        }

        int compilationResult = compiler.run(null, null, null,sourceFile.getPath());
        if(compilationResult == 0){
            System.out.println("Compilation is successful");
        }else{
            System.out.println("Compilation Failed");
        }

        try{
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { sourceFile.toURI().toURL() });
            Class<?> cls = Class.forName("Solution" , true, classLoader);
            Object instance = cls.newInstance();
            Method method = cls.getDeclaredMethod("add", null);
            System.out.println(method.invoke(instance, null));
        }catch(Exception e){
            System.out.println("something wrong");
        }

    }
}

上面的代码问题在于,当我第一次执行时,无法得到结果,似乎下面的代码有异常:
Object instance = cls.newInstance();

然后我执行第二次,它运行良好,所以结论是当我第一次运行时,找不到 Solution 类,导致以下异常。
java.lang.ClassNotFoundException: Solution

"有人可以帮我解决这个问题吗?"

只是想知道为什么不动态加载一个jar文件? - Top.Deck
类加载器只能加载 .class 文件,而不能加载 .java 文件。你无法将 sourceFile 传递给 URLClassLoader。事实上,我相信你甚至不能传递一个 .class 文件;你需要传递包含一个或多个 .class 文件的目录或 .jar 文件的 URL。你传递给 URLClassLoader 的 URL 在功能上与类路径完全相同。 - VGR
1
一个小提示:你甚至不需要将这些东西写入文件。这可以纯粹在内存中完成。请参见https://dev59.com/m3NA5IYBdhLWcg3wgeEd#30038318以获取示例。 - Marco13
3个回答

1
你的问题可能是解决方案中的.class文件被创建在与当前类的.class文件不同的位置。确保创建.class文件的位置包含在当前程序的类路径中,然后你就可以继续了。

1

1) 如果未在同一包中声明,则您的类应具有public修饰符,否则CompliedClass将无法访问它。虽然它们都在默认包中,但不建议这样做。

2) 您的错误在这里:

URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { sourceFile.toURI().toURL() });

这里的sourceFile指的是Solution Java类文件。
根据URLClassLoader java.net.URLClassLoader.newInstance(URL[] urls)的javadoc,它应该指向包含Solution Java类文件的文件夹的url,因为url参数指的是搜索类和资源的URL。

您可以尝试使用两个解释修改后的代码:

public class CompliedClass {
    public static void main(String[] args) {

        String source = "public class Solution{" + "public int add(){" + "return 1+1;" + "}" + "}";

        File folder = new File("./src");
        File sourceFile = new File(folder, "Solution.java");

        try {
            Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(sourceFile.getPath());
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null) {
            System.out.println("JDK required (running inside of JRE)");
        } else {
            System.out.println("you got it!");
        }

        int compilationResult = compiler.run(null, null, null, sourceFile.getPath());
        if (compilationResult == 0) {
            System.out.println("Compilation is successful");
        } else {
            System.out.println("Compilation Failed");
        }

        try {
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] {folder.toURI().toURL() });
            Class<?> cls = Class.forName("Solution", true, classLoader);
            Object instance = cls.newInstance();
            Method method = cls.getDeclaredMethod("add", null);
            System.out.println(method.invoke(instance, null));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("something wrong");
        }

    }
}

输出:

.\src\Solution.java

你做到了!

编译成功

2


你的代码很好,非常感谢。这个路径问题经常让我抓狂。 - crlyw

1
您提供的代码存在几个问题。
  1. 您定义的字符串类应该可以被主类访问,但当前它无法访问。最简单的修复方法是将其设置为public
  2. 您的类加载器正在尝试从源代码本身加载类(“Solution.java” URL作为类路径URL提供)。实际上,URLClassLoader希望是一个jar包或包含类的文件夹的URL。

请参见以下已更正版本:

public class CompliedClass {

  public static void main(String[] args) throws Exception {
    String classDef = ""
        + "public class Solution{"
        + "  public int add(){return 1+1;}"
        + "}";

    Path sourceFile = Paths.get("Solution.java");
    Files.write(sourceFile, classDef.getBytes());
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    if (compiler == null) {
      System.out.println("JDK required (running inside of JRE)");
      System.exit(1);
    } else {
      System.out.println("you got it!");
    }

    int compilationResult = compiler.run(null, null, null, sourceFile.toString());
    if (compilationResult == 0) {
      System.out.println("Compilation is successful");
    } else {
      System.out.println("Compilation Failed");
      System.exit(1);
    }

    URL classPath = sourceFile.toAbsolutePath().getParent().toUri().toURL();
    URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classPath});
    Class<?> cls = Class.forName("Solution", true, classLoader);
    Object instance = cls.newInstance();
    Method method = cls.getDeclaredMethod("add", null);
    System.out.println(method.invoke(instance, null));
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接