GroovyClassLoader对parseClass的调用成功,即使代码未编译也是如此 [英] GroovyClassLoader call to parseClass is successful, even when code does not compile
问题描述
我试图动态地将Groovy脚本作为类加载,但是即使脚本的代码无法编译,也会创建类对象.
I'm trying to dynamically load a Groovy script as a class but the class object is created even when the script's code does not compile.
例如,用于加载Groovy脚本的我的Groovy代码的简化版本如下:
For example, a simplified version of my Groovy code to load the Groovy script is as follows:
GroovyCodeSource src = new GroovyCodeSource(
"blah blah blah",
"Foo.groovy",
GroovyShell.DEFAULT_CODE_BASE
)
new GroovyClassLoader().parseClass(src, true)
很显然,代码blah blah blah
不是合法的Groovy脚本.但是,已经为该动态代码成功创建了一个类对象.根据 GroovyClassLoader用于parseClass
方法的Javadoc 和CompilationFailedException
.
Clearly, the code blah blah blah
isn't a legitimate Groovy script. And yet, a class object is successfully created for this dynamic code. According to GroovyClassLoader's Javadoc for the parseClass
method a CompilationFailedException
should be thrown for cases such as this.
如何仍然为残破的代码创建该类,以及如何根据条件是否可以编译该代码从动态Groovy源代码成功创建一个类?我做了很多研究和实验,但无济于事.
How is it possible that the class is still created for broken code and how can I successfully create a class from dynamic Groovy source code conditionally on whether or not the code will compile? I've done a lot of research and experimentation but to no avail.
推荐答案
这是因为groovy提供了对方法和属性的动态访问,并且就Groovy而言,代码blah blah blah
是有效的.实际上,您是在为Script提供代码(没有类声明).编译之后,您将得到一个扩展了 groovy的类. lang.Script .
That's because groovy provides dynamic access to methods and properties and in terms of Groovy, the code blah blah blah
, is valid. Actually you are providing code for Script (there is no class declaration). After compilation, you will get a class that extends groovy.lang.Script.
所以,让我继续您的代码并向您展示它如何有效...
So, let me continue your code and show you how it could be valid...
GroovyCodeSource src = new GroovyCodeSource(
'blah blah blah',
"Foo.groovy",
GroovyShell.DEFAULT_CODE_BASE
)
def c = new GroovyClassLoader().parseClass(src, true)
println c //class Foo
println c.getSuperclass() //class groovy.lang.Script
def i = c.newInstance()
//i.run() //MissingPropertyException: No such property: blah for class: Foo
i.setBinding([
blah: { x-> return [blah: "x.class =${x.getClass()}"] }
] as Binding)
i.run() //SUCCESS
我也建议您运行groovyconsole
,输入blah blah blah
,按 Ctrl + T ,然后检查为脚本生成了什么类.请注意,您可以在不同的编译/解析阶段之间切换.
I would also advise you to run groovyconsole
, enter blah blah blah
, press Ctrl+T, and check what class was generated for your script. Note that you could switch between different compilation/parsing phases.
可能的解决方法是在方法或类上使用CompileStatic
批注:
A possible workaround is to use the CompileStatic
annotation on the methods or the class:
//compilation of this code will fail with message
//[Static type checking] - The variable [blah] is undeclared.
@groovy.transform.CompileStatic
def f(){
blah blah blah
}
f()
您可以强制GroovyClassLoader
对整个脚本进行静态验证.
You could force GroovyClassLoader
to make static validation for the whole script.
假设您希望脚本仅访问一些预定义的变量/方法,并且希望在编译步骤而不是在运行时进行检查.
Let's imagine you want your scripts to access only some pre-defined variables/methods and you want to check this at compile step and not at runtime.
以下示例显示了如何执行此操作,它将在编译过程中导致blah blah blah
代码失败:
The following example shows how to do that and it will fail the blah blah blah
code during compilation:
import org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder
import org.codehaus.groovy.control.CompilerConfiguration
import groovy.transform.CompileStatic
//your base Script class that declares only valid members
//for example `log`
abstract class MyScript extends groovy.lang.Script{
PrintStream log
}
//create compiler config with base script class
CompilerConfiguration cc = new CompilerConfiguration()
cc.setScriptBaseClass(MyScript.class.getName())
//make static compilation set for class loader
cc = CompilerCustomizationBuilder.withConfig(cc){
ast(CompileStatic)
}
//create classloader with compile config
GroovyClassLoader gcl = new GroovyClassLoader(this.getClass().getClassLoader(),cc)
GroovyCodeSource src = new GroovyCodeSource(
"log.println 'hello world'",
"Foo.groovy",
GroovyShell.DEFAULT_CODE_BASE
)
def c = gcl.parseClass(src, true) //this will fail for 'blah blah blah' source
def i = c.newInstance(log:System.out)
i.run()
P.S. Groovy中还有其他代码转换器.
P.S. There are other code transformers available in Groovy.
这篇关于GroovyClassLoader对parseClass的调用成功,即使代码未编译也是如此的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!