可调用对象的GroovyShell和Binding.setVariable()
我正在用可调用对象的GroovyShell和Binding.setVariable(),groovy,Groovy,我正在用GroovyShell进行一些动态变量创建的实验,遇到了一个问题。首先,工作守则: static def defVar(def glob) { glob.setVariable('test', new Test()) } class MyBinding extends Binding { } class Test { def call() { println("--- hello ---") } } Binding glob = new My
GroovyShell
进行一些动态变量创建的实验,遇到了一个问题。首先,工作守则:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
defVar(glob)
shell.parse('test()').run()
这给了我预期的输出:
--- hello ---
但是,我想在调用getVariable()
时动态调用setVariable()
,如下所示:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable('test')) {
BindingTest.defVar(this)
}
return super.getVariable(name)
}
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable(name)) {
BindingTest.defVar(this)
}
println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
return super.getVariable(name)
}
void setVariable (String name, def val) {
println("setVariable: ${name}: ${val.getClass().getName()}")
super.setVariable(name, val)
}
def getProperty(String name) {
println("getProperty: ${name}: ${super.getProperty(name)}")
return super.getProperty(name)
}
void setProperty (String name, def val) {
println("setProperty: ${name}: ${val.getClass().getName()}")
super.setProperty(name, val)
}
}
但此操作失败,错误如下:
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
at Script1.run(Script1.groovy:1)
at Script1$run.call(Unknown Source)
at BindingTest.run(BindingTest.groovy:23)
当我添加如下跟踪代码时:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable('test')) {
BindingTest.defVar(this)
}
return super.getVariable(name)
}
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable(name)) {
BindingTest.defVar(this)
}
println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
return super.getVariable(name)
}
void setVariable (String name, def val) {
println("setVariable: ${name}: ${val.getClass().getName()}")
super.setVariable(name, val)
}
def getProperty(String name) {
println("getProperty: ${name}: ${super.getProperty(name)}")
return super.getProperty(name)
}
void setProperty (String name, def val) {
println("setProperty: ${name}: ${val.getClass().getName()}")
super.setProperty(name, val)
}
}
在工作情况下,我得到以下输出:
setVariable: test: Test
--- hello ---
在非工作情况下,我得到以下输出:
setVariable: test: Test
getVariable: test: Test
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
...
两个问题:
getVariable
getVariable
返回的Test
对象被拒绝test
,那么这两种方法都可以正常工作。例如,这种变化:
...
static def defVar(def glob) {
glob.setVariable('test', '--- hello ---')
}
...
shell.parse('println(test)').run()
我使用这两种方法得到以下相同的输出:
setVariable: test: java.lang.String
getVariable: test: java.lang.String
setVariable: test: java.lang.String
--- hello ---
不过,我不知道为什么setVariable
会被调用两次。我找不到任何文件来解释这些令人费解的行为。这里有人能解释一下吗
请注意,所有代码片段都经过了简化,以便于演示问题,而不是为了达到其预期目的当您使用属性作为可调用的后备方法时,
Binding.getVariable()
方法不涉及。此行为由元类控制,在您的例子中,它都会驱动执行MetaClassImpl.invokePropertyOrming()
方法。此方法确定
test()
应该调用test.call()
(对于现有属性),或者应该回退到missingMethod()
方法。以下是此方法实现的外观:
private Object invokePropertyForming(对象对象对象、字符串方法名、对象[]原始参数、boolean fromInsideClass、boolean isCallToSuper){
//如果找不到方法,请尝试查找定义为类字段的闭包并运行它
对象值=空;
final MetaProperty MetaProperty=this.getMetaProperty(methodName,false);
if(元属性!=null)
value=metaProperty.getProperty(对象);
否则{
if(映射的对象实例)
value=((映射)对象).get(方法名);
}
if(value instanceof Closure){//此测试确保值!=此如果您更改此确保值!=此
闭包=(闭包)值;
元类delegateMetaClass=closure.getMetaClass();
返回delegateMetaClass.invokeMethod(closure.getClass(),closure,closure\u DO\u CALL\u方法,originalArguments,false,fromInsidelClass);
}
if(脚本的对象实例){
对象bindingVar=((脚本)对象).getBinding().getVariables().get(方法名);
if(bindingVar!=null){
元类bindingVarMC=((MetaClassRegistryImpl)注册表);
返回bindingVarMC.invokeMethod(bindingVar、闭包调用方法、原始参数);
}
}
返回invokeMissingMethod(对象、方法名、原始参数、null、isCallToSuper);
}
资料来源:
现在,请注意分支if(脚本的对象实例)
以及如何检索绑定变量。它尝试使用以下方法从绑定对象检索test
变量:
objectbindingvar=((脚本)对象).getBinding().getVariables().get(方法名);
如果您的代码是:
objectbindingvar=((脚本)对象).getBinding().getVariable(方法名);
相反。但事实并非如此
如果重写getVariables()
方法而不是getVariable(字符串名称)
,则可以使第二个案例正常工作,例如:
class MyBinding extends Binding {
@Override
Map getVariables() {
return super.getVariables() + [
test: new Test()
]
}
}
当然,您的最终实现可能要复杂得多。(例如,您可以先获取super.getVariables()
map,检查缺少哪些变量,并仅在给定变量缺少初始映射时添加默认变量。)但这取决于您自己
可选地,考虑使用<代码>方法> 而不是绑定变量后退。它可以使您的代码更易于阅读和推理。
Szymon,无法理解为什么shell.parse('println“smth”\n test()')。run()
在第一个非工作模式下工作example@daggett当您添加println
时,它会工作,因为groovy.lang.Script
类中的println
方法调用getProperty(“out”)
,在本例中,它将test
添加到绑定变量中。(在本例中,任何调用getProperty()
或getVariable()
方法都会初始化绑定变量中的默认值。)这是一个相关的源代码:感谢您提供了指向底层魔术的指针。有了您提供的信息,我能够使用重写的get()
构建一个自定义Map
,用于检测缺少的变量并动态设置它,效果很好。