这是因为 groovy 提供了对方法和属性的动态访问,并且就 Groovy 而言,代码blah blah blah
是有效的。实际上,您正在为 Script 提供代码(没有类声明)。编译后,你会得到一个扩展groovy.lang.Script的类。
所以,让我继续你的代码并向你展示它是如何有效的......
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并检查为您的脚本生成的类。请注意,您可以在不同的编译/解析阶段之间切换。
一种可能的解决方法是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()
PS Groovy 中还有其他代码转换器。