Groovy mixin не может видеть свои свойства при использовании invokeMethod - PullRequest
0 голосов
/ 02 марта 2012

Я вижу то, что не могу объяснить с помощью миксовинов Groovy (1.8), когда я добавляю метод invokeMethod в один из классов миксинов.

Следующий тест демонстрирует этот эффект:

 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 заставляет это перестать беспокоиться?

РЕДАКТИРОВАТЬ: Если я установлю точки останова в строках 15 и 20, я вижу this как MixinPropertyTest$Foo (id=43) и MixinPropertyTest$Foo (id=75), соответственно. Похоже, что экземпляр Foo, с которым я взаимодействую, изменяется во время вызова MetaMethod.invoke.

Ответы [ 2 ]

1 голос
/ 02 марта 2012

Использование invokeMethod перехватывает все вызовы методов и свойств согласно Использование invokeMethod и getProperty .Если вы хотите, чтобы он прошел до message(), вы должны добавить условие if, чтобы проверить, является ли имя метода «message», или изменить invokeMethod на methodMissing.

РЕДАКТИРОВАТЬ:

Поскольку вы пытаетесь перехватить вызов, я думаю, что проблема может заключаться в том, что переадресация invoke не получает правильный мета-метод.То есть, поскольку вы используете metaClass из this, вы получите MixinInstanceMetaMethod, потому что this.metaClass относится к MixedInMetaClass.Учитывая, что у вас есть MetaMethod, его следует вызывать у владельца метакласса (который должен быть Bar).

Следующий код должен работать:

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()
    }
}

Дальнейшее уточнение:Я думаю, потому что this, который вы передаете в metaMethod.invoke, уже является экземпляром mixin, причина, по которой this изменяется, заключается в том, что metaMethod - это MixinInstanceMetaMethod, который пытается найти экземпляр mixin.Если он не найден, он создает новый экземпляр mixin, поэтому ссылка на this отличается от invokeMethod на message метод.

Эта строка в исходном коде Groovy показывает, что вызов invoke ищет экземпляр миксина.

Эта строка в источнике показывает создание нового экземпляра миксина:https://github.com/groovy/groovy-core/blob/master/src/main/org/codehaus/groovy/reflection/MixinInMetaClass.java#L68

0 голосов
/ 03 марта 2012

Мне удалось разобраться с этим с помощью отладчика.

Поскольку invokeMethod() использовал this (экземпляр Foo), а не объект, владеющий этим миксином, MixinInMetaClass.getMixinInstance() под обложками решает создать новый экземпляр Foo. (Он кэширует экземпляры mixin на основе внешнего объекта и думает, что я ищу экземпляр mixin Foo в экземпляре Foo.)

Похоже, я могу обойти это, заметив, что invokeMethod вызывается для экземпляра OwnedMetaClass:

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 для использования в качестве миксина, кажется неопрятным.

...