Groovy Миксины?

Я пытаюсь смешивать класс в моем приложении Groovy/Grails, и я использую синтаксис, определенный в документах, но я все время получаю сообщение об ошибке.

У меня есть класс домена, который выглядит так:

class Person {
  mixin(ImagesMixin)

  // ...
}

Он компилируется отлично, но по какой-то причине он не будет работать. Файл, содержащий ImagesMixin, находится в моем каталоге /src/groovy/.

Я пробовал его с помощью Groovy версий 1.5.7 и 1.6-RC1 без везения. Кто-нибудь знает, что я делаю неправильно?

StackTrace:

2008-12-30 17:58:25.258::WARN:  Failed startup of context [email protected]{/FinalTransmission,/home/kuccello/Development/workspaces/lifeforce/FinalTransmission/web-app}
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.ExceptionInInitializerError
    at java.security.AccessController.doPrivileged(Native Method)
    at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy:67)
    at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy)
    at Init_groovy$_run_closure6.doCall(Init_groovy:131)
    at RunApp_groovy$_run_closure2.doCall(RunApp_groovy:66)
    at RunApp_groovy$_run_closure2.doCall(RunApp_groovy)
    at RunApp_groovy$_run_closure1.doCall(RunApp_groovy:57)
    at RunApp_groovy$_run_closure1.doCall(RunApp_groovy)
    at gant.Gant.dispatch(Gant.groovy:271)
    at gant.Gant.this$2$dispatch(Gant.groovy)
    at gant.Gant.invokeMethod(Gant.groovy)
    at gant.Gant.processTargets(Gant.groovy:436)
    at gant.Gant.processArgs(Gant.groovy:372)
Caused by: java.lang.ExceptionInInitializerError
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:169)
    at Episode.class$(Episode.groovy)
    at Episode.<clinit>(Episode.groovy)
    ... 13 more
Caused by: groovy.lang.MissingMethodException: No signature of method: static Person.mixin() is applicable for argument types: (java.lang.Class) values: {class ImagesMixin}
    at Broadcast.<clinit>(MyClass.groovy:17)
    ... 17 more
2008-12-30 17:58:25.259::WARN:  Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pluginManager' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.ExceptionInInitializerError:
groovy.lang.MissingMethodException: No signature of method: Person.mixin() is applicable for argument types: (java.lang.Class) values: {class ImagesMixin}
    at Broadcast.<clinit>(Person.groovy:17)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:169)
    at Episode.class$(BelongsToMyClass.groovy)
    at Episode.<clinit>(BelongsToMyClass.groovy)
    at java.security.AccessController.doPrivileged(Native Method)
    at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy:67)
    at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy)
    at Init_groovy$_run_closure6.doCall(Init_groovy:131)
    at RunApp_groovy$_run_closure2.doCall(RunApp_groovy:66)
    at RunApp_groovy$_run_closure2.doCall(RunApp_groovy)
    at RunApp_groovy$_run_closure1.doCall(RunApp_groovy:57)
    at RunApp_groovy$_run_closure1.doCall(RunApp_groovy)
    at gant.Gant.dispatch(Gant.groovy:271)
    at gant.Gant.this$2$dispatch(Gant.groovy)
    at gant.Gant.invokeMethod(Gant.groovy)
    at gant.Gant.processTargets(Gant.groovy:436)
    at gant.Gant.processArgs(Gant.groovy:372)
2008-12-30 17:58:25.271::INFO:  Started [email protected]:8080

Ответ 1

Так как Groovy 1.6 вы можете применить mixin во время компиляции к классу с помощью аннотации

@Mixin(ImagesMixin)
class Person {
}

Или вы можете применить mixin во время выполнения следующим образом:

def myMixin = ImagesMixin
Person.mixin myMixin

Последний подход более динамичен, поскольку класс для mixin можно определить во время выполнения. Дополнительная информация о Groovy mixins доступна здесь.

По моему опыту, многие метапрограммы классов домена просто не работают. Я точно не знаю, почему, но подозреваю, что эти классы уже очень мета-запрограммированы временем выполнения Grails. Вообще мой подход

  • Попробуйте метапрограммирование в POGO в консоли Groovy
  • Если это работает, попробуйте его в классе без домена в консоли Grails.
  • Если это работает, попробуйте его в классе домена в консоли Grails. Если это не работает, то это должно быть потому, что это класс домена (а не проблема с синтаксисом). На данный момент целесообразно попытаться найти другой способ достижения своей цели. Если это невозможно, используйте комбинацию списка рассылки Grails и/или Stackoverflow и/или исходный код Grails, чтобы попытаться выполнить метапрограммирование.

Ответ 2

Я не думаю, что вы используете правильный синтаксис mixin. Попробуйте следующее:

class MyMixin {
    static doStuff(Person) {
        'stuff was done'
    }
}

class Person {}

Person.mixin MyMixin

new Person().doStuff()

Ответ 3

Я предполагаю, что вы видели там скорее предложение, чем функцию;) Groovy не поддерживает mixins из коробки таким образом еще (если когда-либо). Но есть сторонняя библиотека, которая может использоваться для эмуляции такого поведения: Injecto. И mixins можно определить с помощью AST-Macros в версии 1.6 Groovy (что еще не окончательно).

Вы всегда должны проверить, читаете ли вы документы из реального проекта Groovy или из проекта GroovyJSR (это скорее место, где собираются предложения).

Другой способ - использовать простой MOP для ввода поведения в классы Groovy путем изменения метаклассов.

Приветствия

Ответ 4

В случае, если это помогает кому угодно, следуя из комментария @virtualeyes выше, я обнаружил, что

Person.doStuff()

завершается сбой, если вы не вызываете следующее:

new Person().doStuff()
Person.doStuff()

после чего статический метод в классе, похоже, работает (для меня, используя Grails 2.2.4), я предполагаю, что это связано с инициализацией класса или что-то в этом роде, но я пробовал:

Person.metaClass.initialize()
Person.doStuff()

и это не сработало!

Ответ 5

FYI: В Grails существует такая вещь, как "встроенные" домены, но у нее есть проблемы. Здесь вы можете логически включать один домен как часть другого и иметь все поля в физическом виде в одной таблице DB. Например, если вы обнаружите, что у вас есть одинаковое подмножество полей, отображаемых в нескольких таблицах, например, адрес улицы/город/штат/почтовый индекс, вы можете определить домен StreetAddress и вставить его. Одной из текущих проблем является то, что Grails все равно будет создавать таблицу street_address в дополнение к встраиванию своих полей в другие таблицы (если вы не играете в трюки). Полагаю, что для этого есть ожидающие отладки.

Ответ 6

Объекты домена Grails уже метапрограммированы. Вместо groovy mixin try:

@grails.util.Mixin(ImagesMixin)
    class Person {
}