在Java中运行时编译Groovy类

Col*_*nMc 9 java groovy runtime-compilation

我成功地能够在运行时在Java中编译Groovy并将其存储在数据库中并将其拉出来.如果它有内部类或内部枚举,我无法编译Groovy类.有没有人成功编译这样的Groovy代码并包含内部类/枚举并能够通过类名拉出脚本?

例如,我想加载下面显示的包含内部类的"Test"脚本,并在运行时运行脚本.

编译代码:

public byte[] compileGroovyScript(final String className, final String script) {
    byte[] compiledScriptBytes = null;
    CompilationUnit compileUnit = new CompilationUnit();
    compileUnit.addSource(className, script);
    compileUnit.compile(Phases.CLASS_GENERATION);

    for (Object compileClass : compileUnit.getClasses()) {
        GroovyClass groovyClass = (GroovyClass) compileClass;
        compiledScriptBytes = groovyClass.getBytes();
    }

    return compiledScriptBytes;
}
Run Code Online (Sandbox Code Playgroud)

拉出脚本的代码:

public Class getGroovyScript(final String className, final byte[] script) {
    Class clazz = null;

    try (GroovyClassLoader classLoader = new GroovyClassLoader(this.getClass().getClassLoader())) {
        clazz = classLoader.defineClass(className, script);
    } catch (IOException e) {
    } catch (Exception e) {
    }

    return clazz;
}
Run Code Online (Sandbox Code Playgroud)

运行脚本的代码:

Class groovyClass = app.getGroovyScript(className, compiledScript);
TestScript script = (TestScript) groovyClass.newInstance();
System.out.println(script.getMessage());
Run Code Online (Sandbox Code Playgroud)

Groovy脚本:

import com.groovy.groovy.TestScript

class Test implements TestScript {

    String getMessage() {
        [1..10].each(){
            println it
        }
        return "Jello"
    }
}
Run Code Online (Sandbox Code Playgroud)

Jef*_*own 10

从描述中不清楚为什么你自己编译.如果你可以让Groovy为你做,那么整个事情可以简化为这样的事情:

String script = // string containing the script you want to parse

GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
Class theParsedClass = groovyClassLoader.parseClass(script);
Run Code Online (Sandbox Code Playgroud)

  • 我提到我想编译Groovy脚本并将它们放在数据库中.这是因为性能,所以当我想运行一个脚本时,我不必多次编译它.脚本每分钟运行一次,每分钟编译一个脚本似乎效率低下. (2认同)

Dea*_*ler 5

好的,这可能有点晚了,但希望它可以帮助下一个人。我认为您需要为每个 groovy 类保存一个 List,然后是 cl.defineClass,最后是 cl.loadClass。我认为 groovy 有时会编译为类列表,基本上如下所示,当我 addSource() 时,我添加一个类,然后循环遍历该文件中所有生成的类。

这是我目前正在运行的代码(虽然我没有尝试过稍后保存和重新加载)

    GroovyClassLoader cl = new GroovyClassLoader();
    CompilationUnit compileUnit = new CompilationUnit();
    compileUnit.addSource(scriptCode.getClassName(), scriptCode.getScriptSourceCode());
    compileUnit.compile(Phases.CLASS_GENERATION);
    compileUnit.setClassLoader(cl);

    GroovyClass target = null;
    for (Object compileClass : compileUnit.getClasses()) {
        GroovyClass groovyClass = (GroovyClass) compileClass;
        cl.defineClass(groovyClass.getName(), groovyClass.getBytes());
        if(groovyClass.getName().equals(scriptCode.getClassName())) {
            target = groovyClass;
        }
    }

    if(target == null) 
        throw new IllegalStateException("Could not find proper class");

    return cl.loadClass(target.getName());
Run Code Online (Sandbox Code Playgroud)

记下 cl.defineClass 调用,它将类放入类加载器中,因此当它被查找时(枚举或内部类),它将在那里。

所以现在我认为你不需要创建你自己的类加载器(尽管你避免使用无用的defineClass,直到你自己的类加载器需要它,它可以是有用的和更高的性能)。