我成功地能够在运行时在Java中编译Groovy并将其存储在数据库中并将其拉出来。如果它有内部类或内部枚举,我就无法编译Groovy类。有没有人成功编译这样的Groovy代码并包含内部类/枚举并能够通过类名拉出脚本?
例如,我想加载"测试"下面显示的脚本包含内部类并在运行时运行脚本。
编译器代码:
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;
}
将脚本拉出来的代码:
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;
}
运行脚本的代码:
Class groovyClass = app.getGroovyScript(className, compiledScript);
TestScript script = (TestScript) groovyClass.newInstance();
System.out.println(script.getMessage());
Groovy脚本:
import com.groovy.groovy.TestScript
class Test implements TestScript {
String getMessage() {
[1..10].each(){
println it
}
return "Jello"
}
}
答案 0 :(得分:8)
从描述中不清楚为什么要自己编译。如果你能让Groovy为你做,那么整个事情可以简化为:
String script = // string containing the script you want to parse
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
Class theParsedClass = groovyClassLoader.parseClass(script);
答案 1 :(得分:4)
好吧这可能有点晚了,但希望它有助于下一个人。我认为你需要为每个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());
注意cl.defineClass调用,它将类放在类加载器中,所以当它被查找时(枚举或内部类),它将在那里。
所以现在我认为你不需要创建自己的类加载器(尽管你需要使用自己的类加载器来避免无用的defineClass,这可能是有用且更高效的。)
答案 2 :(得分:0)
这里为了简单起见放弃了任何错误处理,但这可能是你想要的:
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);
List classes = compileUnit.getClasses();
GroovyClass firstClass = (GroovyClass)classes.get(0);
compiledScriptBytes = firstClass.getBytes();
return compiledScriptBytes;
}
答案 3 :(得分:0)
根据您的要求,您可能希望提供对内部类的访问,并且您可以使用类似这样的内容来查找具有匹配名称的类,而不是假设第一个类:
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;
if(className.equals(groovyClass.getName())) {
compiledScriptBytes = groovyClass.getBytes();
break;
}
}
return compiledScriptBytes;
}
答案 4 :(得分:0)
我自己也遇到了这个问题但是在运行时刚刚完成了一个按需java编译器,我相信你遇到了我在这段代码中解决的相同问题
webpieces / runtimecompile是一个使用eclipse编译器的可重用的按需java编译器。
现在,对于groovy,我认为你遇到了这种情况
1. you compile ONE script
2. this results in 'multiple' class file objects (I think) just like mine did
3. This is where you need to store EACH in the database SEPARATELY
4. Then you need a classloader that tries to lookup the 'inner classes' when jvm asks for it
5. finally you do a yourclassLoader.loadApplicationClass (much like the one in CompileOnDemandImpl.java in the project above
6. To be clear, step 5 causes step 4 to happen behind the scenes (and that is what is confusing).
如果您单步执行测试用例AnonymousByteCacheTest,它几乎就是这样做的。
您不需要安装ANYTHING来运行该项目的构建,只需克隆它和" ./ gradlew test"并将通过" ./ gradlew eclipse"或" ./ gradlew idea"它会生成IDE文件,因此您可以逐步完成它。
非常相似。我正试图让groovy版本自己工作。