Groovy覆盖默认调用方法

时间:2018-04-04 20:17:27

标签: gradle groovy closures dsl gradle-plugin

我有以下groovy类作为我的gradle插件的一部分:

class MyClass {
    final Expando someOptions

    MyClass() {
        someOptions = new Expando()
    }

    def call(Closure configure) {
        configure.delegate = someOptions
        configure.resolveStrategy = Closure.DELEGATE_ONLY
        configure()
    }
}

现在我希望用户能够通过向其添加额外属性来配置此类,但这些属性应存储在someOptions中。

我尝试在课堂上这样做:

def call(final Closure configure) {
    configure.delegate = someOptions
    configure.resolveStrategy = Closure.DELEGATE_ONLY
    configure()
}

插件的用户可以:

myClass {
    hello='world'
}

但是,gradle似乎并不理解hello实例上不存在myClass属性,而是在类中的someOptions上。每当我使用上述内容时,我都会在hello实例中出现MyClass错误。

我该怎么做?有可能吗?

FWIW,它在groovy控制台中工作,但不在gradle中。

1 个答案:

答案 0 :(得分:1)

您在插件中定义的任何类都不会直接在Gradle中使用,而是由Gradle包含在代理类中。例如,

  

Gradle将为实际的类实现创建一个代理类,并添加(以及其他)属性setter方法。该方法具有属性的名称,并且具有与该属性相同类型的单个参数。它与Groovy已添加的setPropertygetProperty方法不同。例如,如果我们的任务具有名称为String类型的消息的属性,则Gradle会将方法message(String)添加到代理类。 (Source

这就是为什么你可以在Gradle scrips中省略分配符号的原因:

task myTask {
    myProperty true      // uses Gradle generated method
    myProperty = true    // uses Groovy generated setter
}

Gradle还添加了一个与您类似的方法,以允许配置DSL中的任何对象:

myExtension {
    // this works thanks to Gradle
}

如果没有这种代理方法,就必须使用Groovy语言中的with(Closure)方法来处理任何块:

myExtension.with {
    // this works thanks to Groovy
}

似乎此代理方法会覆盖示例的call(Closure)方法。

要解决此问题,您可以在someOptions上的Groovy中使用Delegate注释。这将使MyClass实例可以使用其所有属性。您也可以在someOptions上注册MyClass作为约定。

修改

您可以看到,在更改了call方法的名称并明确调用它之后,通过比较当前示例和第二个堆栈跟踪的堆栈跟踪永远不会调用您的方法(您需要使用另一个属性来得到同样的例外)。