GroovyClassLoader 对 parseClass 的调用是成功的,即使代码没有编译
GroovyClassLoader call to parseClass is successful, even when code does not compile
我正在尝试将 Groovy 脚本作为 class 动态加载,但即使脚本代码未编译,也会创建 class 对象。
例如,我的Groovy加载Groovy脚本的代码简化版如下:
GroovyCodeSource src = new GroovyCodeSource(
"blah blah blah",
"Foo.groovy",
GroovyShell.DEFAULT_CODE_BASE
)
new GroovyClassLoader().parseClass(src, true)
显然,代码 blah blah blah
不是合法的 Groovy 脚本。然而,为这个动态代码成功创建了一个 class 对象。根据 GroovyClassLoader's Javadoc for the parseClass
method,对于这种情况,应该抛出 CompilationFailedException
。
如何仍然为损坏的代码创建 class 以及如何根据代码是否有条件地从动态 Groovy 源代码成功创建 class会编译吗?我做了很多研究和实验,但无济于事。
这是因为 groovy 提供了对方法和属性的动态访问,并且就 Groovy 而言,代码 blah blah blah
是有效的。实际上,您正在为脚本提供代码(没有 class 声明)。编译后,你会得到一个 class extends 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,然后检查什么class 是为您的脚本生成的。请注意,您可以在不同的 compilation/parsing 阶段之间切换。
一个可能的解决方法是在方法上使用 CompileStatic
注释或 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
对整个脚本进行静态验证。
假设您希望您的脚本仅访问一些预定义的 variables/methods 并且您希望在编译步骤而不是 运行 时检查它。
以下示例显示了如何执行此操作,它会在编译期间使 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()
P.S。 Groovy.
中还有其他代码转换器可用
我正在尝试将 Groovy 脚本作为 class 动态加载,但即使脚本代码未编译,也会创建 class 对象。
例如,我的Groovy加载Groovy脚本的代码简化版如下:
GroovyCodeSource src = new GroovyCodeSource(
"blah blah blah",
"Foo.groovy",
GroovyShell.DEFAULT_CODE_BASE
)
new GroovyClassLoader().parseClass(src, true)
显然,代码 blah blah blah
不是合法的 Groovy 脚本。然而,为这个动态代码成功创建了一个 class 对象。根据 GroovyClassLoader's Javadoc for the parseClass
method,对于这种情况,应该抛出 CompilationFailedException
。
如何仍然为损坏的代码创建 class 以及如何根据代码是否有条件地从动态 Groovy 源代码成功创建 class会编译吗?我做了很多研究和实验,但无济于事。
这是因为 groovy 提供了对方法和属性的动态访问,并且就 Groovy 而言,代码 blah blah blah
是有效的。实际上,您正在为脚本提供代码(没有 class 声明)。编译后,你会得到一个 class extends 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,然后检查什么class 是为您的脚本生成的。请注意,您可以在不同的 compilation/parsing 阶段之间切换。
一个可能的解决方法是在方法上使用 CompileStatic
注释或 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
对整个脚本进行静态验证。
假设您希望您的脚本仅访问一些预定义的 variables/methods 并且您希望在编译步骤而不是 运行 时检查它。
以下示例显示了如何执行此操作,它会在编译期间使 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()
P.S。 Groovy.
中还有其他代码转换器可用