这是因为 groovy 提供了对方法和属性的动态访问,就 Groovy 而言,代码blah blah blah
, 已验证。实际上,您正在为脚本提供代码(没有类声明)。编译后,你会得到一个扩展的类groovy.lang.Script http://docs.groovy-lang.org/latest/html/api/groovy/lang/Script.html.
那么,让我继续您的代码并向您展示它如何有效......
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
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
方法或类上的注释:
//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
对整个脚本进行静态验证。
假设您希望脚本仅访问一些预定义的变量/方法,并且您希望在编译步骤而不是运行时检查这一点。
下面的例子展示了如何做到这一点,但它会失败blah blah blah
编译期间的代码:
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()
附: Groovy 中还有其他可用的代码转换器。