Groovy Mixins? - PullRequest
       33

Groovy Mixins?

10 голосов
/ 08 января 2009

Я пытаюсь добавить класс в моем приложении 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 org.mortbay.jetty.webapp.WebAppContext@562791{/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 SelectChannelConnector@0.0.0.0:8080

Ответы [ 7 ]

12 голосов
/ 14 августа 2009

Начиная с Groovy 1.6, вы можете применять миксин во время компиляции к классу, используя аннотацию

@Mixin(ImagesMixin)
class Person {
}

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

def myMixin = ImagesMixin
Person.mixin myMixin

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

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

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

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

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

class Person {}

Person.mixin MyMixin

new Person().doStuff()
4 голосов
/ 08 января 2009

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

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

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

Приветствия

2 голосов
/ 10 августа 2013

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

Person.doStuff()

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

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

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

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

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

1 голос
/ 21 января 2009

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

0 голосов
/ 23 октября 2018

Используйте Черты!

Черты - причина, по которой они убрали поддержку миксинов, потому что это просто лучше. Это в основном абстрактные классы, которые реализованы. Позволяет вам использовать несколько и работать с ними как с частичными классами.

trait A {
    void printSomething() {
        println "foobar"
    }
}

class B implements A {
    void printAnything() {
        printSomething()
    }
}

new B().printAnything() 

Попробуйте!

0 голосов
/ 12 сентября 2014

Объекты домена Grails уже сильно метапрограммированы. Вместо заводного миксина попробуйте:

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