如何说服GroovyShell通过eval()调用维护状态?
时间:2020-03-05 18:48:09 来源:igfitidea点击:
我正在尝试使用Groovy为我的应用程序创建交互式脚本/宏模式。该应用程序是OSGi,脚本可能需要的很多信息都不是预先知道的。我认为我可以使用GroovyShell并多次调用eval(),以在加载OSGi捆绑包时不断追加到名称空间。 GroovyShell通过多个eval调用维护变量状态,但不维护类定义或者方法。
目标:在启动期间创建基类。在OSGi捆绑软件加载时,根据需要创建派生类。
解决方案
回答
这可能是我们在寻找什么?
从Groovy in Action
def binding = new Binding(x: 6, y: 4) def shell = new GroovyShell(binding) def expression = '''f = x * y''' shell.evaluate(expression) assert binding.getVariable("f") == 24
适当使用Binding可以使我们保持状态吗?
回答
我不确定在eval之间不存在声明的类的含义,以下两个脚本在逐个回避时可以按预期工作:
class C {{println 'hi'}} new C()
...
new C()
但是,方法绑定到声明它们的类,并且GroovyShell为每个实例创建一个新类。如果不需要任何脚本的返回值,并且它们是真正的脚本(不是具有主方法的类),则可以将以下内容添加到每个评估的脚本的末尾。
Class klass = this.getClass() this.getMetaClass().getMethods().each { if (it.declaringClass.cachedClass == klass) { binding[it.name] = this.&"$it.name" } }
如果我们依赖返回值,则可以在评估过程中手动管理评估并运行脚本(警告,未经测试的代码紧随其后,仅用于说明性用途)...
String scriptText = ... Script script = shell.parse(scriptText) def returnValue = script.run() Class klass = script.getClass() script.getMetaClass().getMethods().each { if (it.declaringClass.cachedClass == klass) { shell.context[it.name] = this.&"$it.name" } } // do whatever with returnValue...
最后,我要确保我们知道。静态类型变量不保存在评估之间,因为它们没有存储在绑定中。因此,在先前的脚本中,变量" klass"将不会在脚本调用之间保留,而是消失。要纠正这一点,只需在首次使用所有变量时删除类型声明,这意味着它们将被读取并写入绑定中。
回答
最终在每次脚本编译之前注入代码。最终目标是,用户编写的脚本具有特定于域的语言可供使用。