GroovyClassLoader对parseClass的调用成功,即使代码未编译也是如此 [英] GroovyClassLoader call to parseClass is successful, even when code does not compile

查看:657
本文介绍了GroovyClassLoader对parseClass的调用成功,即使代码未编译也是如此的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图动态地将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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆