【问题标题】:Create a java class dynamically and compile and instantiate at run time动态创建java类并在运行时编译实例化
【发布时间】:2020-06-25 03:16:09
【问题描述】:

我有一个String,我需要将其转换为 java 类,在运行时编译并创建一个实例: 假设我的String 是:

String s = " public class Test {
  public Double add(Double x, Double y){
    return (x+y);
  }
}"

如何将其转换为类Test.class,实例化它并在运行时调用方法add(Double x, Double y)

我阅读了有关 Byte Buddy 的信息,但我看到的示例已经定义了一个类。在上述情况下,任何人都可以举一个例子,我如何使用 ByteBuddy 或任何其他可以实现这一目标的库?

关于如何将此String 转换为可编译和可实例化的 java 类的任何输入或建议都会有所帮助。

【问题讨论】:

  • 您将不得不以某种方式调用 Java 编译器。这意味着将其与应用程序捆绑在一起。此时,您可以通过 shell 或其他您喜欢的方式调用它。
  • 您能解释一下为什么需要这样做吗?
  • 有很多例子,比如thisthatherethere,甚至解决了潜在的后续问题。

标签: java dynamic runtime bytecode byte-buddy


【解决方案1】:

我们可以使用这样的东西吗:

package com.demo;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;

public class StringToClass
{
    public static void main(String[] args)
    {
        String s = "import com.demo.FormulaAPI; public class FormulaExecutor" +
                " { public Double formula1(FormulaAPI apiReference)" +
                " { System.out.println(apiReference.evaluate(\"10.10\"));  return apiReference.evaluate(\"10.10\"); } }";
        try
        {
            dynamicClass(s, "FormulaExecutor");
        } catch (IOException | NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e)
        {
            e.printStackTrace();
        }

    }

    static void dynamicClass(String sourceCode, String className) throws IOException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException
    {
        File parent = new File(System.getProperty("user.dir"));
        File sourceFile = new File(parent, className + ".java");
        sourceFile.deleteOnExit();

        FileWriter writer = new FileWriter(sourceFile);
        writer.write(sourceCode);
        writer.close();

        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);
        File parentDir = sourceFile.getParentFile();
        standardJavaFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(parentDir));
        Iterable<? extends JavaFileObject> compilationUnits = standardJavaFileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
        javaCompiler.getTask(null, standardJavaFileManager, null, null, null, compilationUnits).call();
        standardJavaFileManager.close();

        URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[] {parentDir.toURI().toURL()});
        Class<?> dynamicClass = urlClassLoader.loadClass(className);


        Method formula1 = dynamicClass.getDeclaredMethod("formula1", FormulaAPI.class);
        formula1.invoke(dynamicClass.newInstance(), new Object[] {new FormulaAPI()});
    }


}

package com.demo;

public class FormulaAPI
{
    public Double evaluate(String str)
    {
        return Double.valueOf(str);
    }
}

目前,方法名称是硬编码的

        Method addMethod = dynamicClass.getDeclaredMethod("add", Double.class, Double.class);

我们甚至可以在运行时使用反射生成它

我们可以在源代码中导入类。

【讨论】:

  • 感谢您提出解决方案。我尝试了代码,但得到错误 java.lang.ClassNotFoundException: on the line : urlClassLoader.loadClass
  • 我在本地机器上运行它。它对我有用。我正在使用 macbook 和 intellij 进行开发。你能分享一下堆栈跟踪吗? @javaseeker
  • 您提到的示例运行良好。我看到的是一个警告-下面的堆栈跟踪,知道为什么吗?由于 cmets 有字数限制,无法粘贴整个堆栈跟踪,我将在此处部分粘贴
  • <..the path..>FormulaExecutor.java:1: 警告:由于(很可能)类加载器问题,无法初始化 javac 处理器:java.lang.NoClassDefFoundError: com/sun /tools/javac/processing/JavacProcessingEnvironment
  • 我的源字符串是:public class FormulaExecutor { public Double formula1(FormulaAPI apiReference) { return apiReference.evaluate("L"); } }
【解决方案2】:

Byte Buddy 在字节码级别工作,不处理源代码。您可以为此目的使用 Javassist,它提供了对源代码的有限处理,因为它附带了自己的编译器。

或者,按照建议使用 Java 编译器 API。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 2020-09-24
    • 1970-01-01
    • 2020-12-23
    • 2011-10-15
    相关资源
    最近更新 更多