当我将invokeMethod
方法放到其中一个mixin类上时,我看到了无法用Groovy(1.8)mixins解释的内容。
以下测试证明了这种效果:
1: import java.net.Socket
2:
3: import org.junit.Test
4: import static org.junit.Assert.*
5:
6: class MixinPropertyTest {
7: static class Foo {
8: def message
9:
10: Object invokeMethod(String name, args) {
11: if (name != "println") {
12: println "invokeMethod sees ${message}"
13: println "invoking ${name}"
14: }
15: def metaMethod = metaClass.getMetaMethod(name, args)
16: metaMethod?.invoke(this, args)
17: }
18:
19: String message() {
20: message
21: }
22: }
23:
24: @Mixin(Foo)
25: static class Bar {
26: }
27:
28: @Test
29: void test() {
30: assertEquals 'hello', new Bar(message: 'hello').message()
31: }
32: }
此测试失败,输出如下:
invokeMethod sees hello
invoking message
但是,如果我剪掉invokeMethod
,它会通过。 invokeMethod
那里的this
怎么会导致停止工作?
编辑:如果我在第15行和第20行设置了断点,我会分别将MixinPropertyTest$Foo (id=43)
视为MixinPropertyTest$Foo (id=75)
和Foo
。在MetaMethod.invoke
调用过程中看起来好像我正在与{{1}}实例进行交互。
答案 0 :(得分:1)
根据Using invokeMethod and getProperty使用invokeMethod
拦截 所有方法和属性调用 。如果您希望它传递到message()
,则应添加if子句以检查方法名称是“message”还是将invokeMethod
更改为methodMissing
。
修改强>
由于你试图拦截这个电话,我认为问题可能是转发invoke
未获得正确的metaMethod。也就是说,由于您使用的是来自metaClass
的{{1}},因此this
会引用MixinInstanceMetaMethod
this.metaClass
。鉴于您有MixedInMetaClass
,应该在metaClass所有者(应该是MetaMethod
)上调用它。
以下代码应该有效:
Bar
进一步澄清:我认为因为您传递给metaMethod.invoke的import org.junit.Test
import static org.junit.Assert.*
class MixinPropertyTest {
static class Foo {
def message
def invokeMethod(String name, args) {
System.out.println "invokeMethod sees ${message}"
System.out.println "invoking ${name}"
def metaMethod = this.metaClass.getMetaMethod(name, args)
metaMethod?.invoke(this.metaClass.owner, args)
}
String message() {
message
}
}
@Mixin(Foo)
static class Bar {
}
@Test
void test() {
assertEquals 'hello', new Bar(message: 'hello').message()
}
}
已经是mixin实例,this
更改的原因是因为metaMethod是this
它试图查找mixin实例。如果未找到,则会创建新的mixin实例,这就是MixinInstanceMetaMethod
对this
到invokeMethod
方法的引用不同的原因。
This line in the Groovy source显示调用message
会查找mixin实例。
源代码中的这一行显示了新mixin实例的创建:https://github.com/groovy/groovy-core/blob/master/src/main/org/codehaus/groovy/reflection/MixinInMetaClass.java#L68
答案 1 :(得分:0)
我设法用调试器解决了这个问题。
由于invokeMethod()
正在使用this
(Foo
的实例),而不是拥有该mixin的对象,因此MixinInMetaClass.getMixinInstance()
决定创建Foo
的新实例{1}}。 (它根据外部对象缓存mixin实例,它认为我在Foo
的实例中寻找Foo
的mixin实例。)
我似乎可以通过注意在OwnedMetaClass的实例上调用invokeMethod来解决这个问题:
class Foo {
// ...
Object invokeMethod(String name, args) {
def target = this
if (metaClass instanceof OwnedMetaClass) {
target = metaClass.owner
}
MetaMethod metaMethod = target.metaClass.getMetaMethod(name, args)
metaMethod?.invoke(target, args)
}
// ...
}
这似乎应该由Groovy自动处理。要求类在invokeMethod
中有额外的代码才能用作mixin,这似乎是非Groovy。