当涉及invokeMethod时,Groovy mixin无法查看其属性

时间:2012-03-02 06:46:50

标签: groovy mixins

当我将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}}实例进行交互。

2 个答案:

答案 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实例,这就是MixinInstanceMetaMethodthisinvokeMethod方法的引用不同的原因。

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()正在使用thisFoo的实例),而不是拥有该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。