【问题标题】:Groovy Mixins?Groovy Mixin?
【发布时间】:2010-09-30 08:24:15
【问题描述】:

我正在尝试在我的 Groovy/Grails 应用程序中混入一个类,我正在使用 the syntax defined in the docs,但我不断收到错误消息。

我有一个如下所示的域类:

class Person {
  mixin(ImagesMixin)

  // ...
}

它编译得很好,但由于某种原因它不起作用。包含 ImagesMixin 的文件位于我的/src/groovy/ 目录中。

我已经使用 Groovy 版本 1.5.7 和 1.6-RC1 尝试过,但没有任何运气。有谁知道我做错了什么?

堆栈跟踪:

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

【问题讨论】:

  • 在开始编写 mixins 代码之前,请务必考虑性能影响。

标签: grails groovy mixins


【解决方案1】:

我猜你所看到的与其说是一个特性,不如说是一个提议;)Groovy 还不支持以这种方式开箱即用的mixin(如果有的话)。但是有一个第 3 方库可以用来模拟这种行为:Injecto。并且可以在 1.6 版本的 Groovy(还不是最终版本)中使用 AST-Macros 定义 mixins。

您应该始终检查您是从真正的 groovy 项目还是从 GroovyJSR 项目(这是一个收集提案的地方)阅读文档。

另一种方法是使用普通的 MOP 通过修改元类将行为注入到 groovy 类中。

干杯

【讨论】:

    【解决方案2】:

    仅供参考:现在 Grails 中有“嵌入式”域这样的东西,但它存在问题。在这里,您可以在逻辑上将一个域包含为另一个域的一部分,并使其所有字段都物理地出现在一个 DB 表中。例如,如果您发现在多个表中出现了相同的字段子集,例如街道地址/城市/州/邮编,您可以定义 StreetAddress 域并将其嵌入。当前的问题之一是 Grails 除了将其字段嵌入到其他表中之外,仍将创建一个 street_address 表(除非您玩诡计)。似乎有提交的补丁等待处理。

    【讨论】:

      【解决方案3】:

      我认为您没有使用正确的 mixin 语法。试试这个:

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

      【讨论】:

      • doStuff() 真的应该是静态的吗?
      • 这个解决方案不起作用:Person.doStuff() 失败,这是创建静态方法的全部意义,在静态上下文中使用它,对吧? ;--) 从 1.8 开始,Groovy 静态 mixins 仍然被破坏,也许在 2.0 中我们会有工作的静态 mixins。在此之前,您必须将 MOP 父静态元数据元类放到子类上,这不太理想。好与坏,Groovy 总体上很棒,而且,在 imo 中,它正在上升。
      【解决方案4】:

      从 Groovy 1.6 开始,您可以在编译时使用注释将 mixin 应用于类

      @Mixin(ImagesMixin)
      class Person {
      }
      

      或者你可以像这样在运行时应用 mixin:

      def myMixin = ImagesMixin
      Person.mixin myMixin
      

      后一种方法更加动态,因为可以在运行时确定要混入的类。有关 Groovy mixins 的更多信息,请访问here

      根据我的经验,很多领域类的元编程根本行不通。我不完全知道为什么,但怀疑这是因为这些类已经被 Grails 运行时进行了非常大量的元编程。一般来说,我的方法是

      • 在 Groovy 控制台中尝试在 POGO 上进行元编程
      • 如果可行,请在 Grails 控制台中的非域类上尝试
      • 如果可行,请在 Grails 控制台中的域类上尝试。如果它不起作用,那么它一定是因为它是一个域类(而不是语法问题)。在这一点上,建议尝试找到另一种实现目标的方法。如果这不可行,请结合使用 Grails 邮件列表和/或 Stackoverflow 和/或 Grails 源代码来尝试使元编程正常工作。

      【讨论】:

        【解决方案5】:

        如果这对任何人都有帮助,请按照上面的@virtualeyes 评论,我发现

        Person.doStuff()
        

        除非您先调用以下命令否则失败:

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

        之后,类上的静态方法似乎确实有效(对我来说,使用 Grails 2.2.4)我猜这与初始化类有关,但我尝试了:

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

        那没用!

        【讨论】:

          【解决方案6】:

          Grails 域对象已经被大量元编程。 而不是 groovy mixin 尝试:

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

          【讨论】:

          • 还需要注意的是,grails 添加的 Gorm 方法是在通过 mixin 添加方法之后出现的,您将无法从混合方法中调用它们,因此您可能需要添加域如果您打算引用动态查找器或休眠会话或其他任何东西,对象作为任何混合方法的参数。
          【解决方案7】:

          使用特征!

          特性是他们删除对 mixin 的支持的原因,因为这样更好。它们基本上是实现的抽象类。允许您使用多个并将它们作为部分类进行操作。

          trait A {
              void printSomething() {
                  println "foobar"
              }
          }
          
          class B implements A {
              void printAnything() {
                  printSomething()
              }
          }
          
          new B().printAnything() 
          

          试试看!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-01-30
            • 2011-11-06
            • 2013-05-18
            • 1970-01-01
            • 1970-01-01
            • 2011-07-02
            • 2014-06-01
            • 2011-01-30
            相关资源
            最近更新 更多