0

我正在查看闭包范围,发现输出计数器很直观,这段代码在build.gradle文件中

plugins {
    id 'java'
}

sourceSets {

    println 'source sets closure scopes this,owner, delegate'
    println this.toString() // output: root project 'multigradle'
    println owner.toString() // output: DynamicObject for SourceSet container
    println delegate.toString() // output: SourceSet container
    
}

为什么owner不等于this,gradle会克隆闭包吗?

PS:对于将来会阅读它的任何人,“multigradle”是我的 gradle 项目名称。

4

1 回答 1

0

TL;博士;

基本上在 gradle 源中有一个类似这样的方法:

 public Object sourceSets(Closure closure) {
   // do stuff like configuring the closure
   closure.call()
 }

所以当你打电话时:

sourceSets { 
  some code
}

(顺便说一句,这与 call 相同sourceSets({ some code }),只是去掉了括号,这在 groovy 中是可以的)

sourceSets调用该方法时,“某些代码”不会立即执行。Gradle 可以选择在他们决定何时执行它。具体来说,他们可以(并且确实)在实际执行闭包之前配置所有者和委托等内容。

更长的版本

原来文件中的sourceSets方法build.gradle实际上是由插件添加的,例如 java/kotlin/groovy 插件。

作为示例,我们可以查看 java 插件和DefaultJavaPluginConvention 类,它具有以下代码:

    private final SourceSetContainer sourceSets;

    @Override
    public Object sourceSets(Closure closure) {
        return sourceSets.configure(closure);
    }

sourceSets { ... }这是在您输入build.gradle文件时调用的方法。它获得了闭包并继续将其交给configure源集容器的方法。请注意,我们还没有执行闭包,我们只是将它作为未执行的代码块传递。

如果我们稍微挖掘一下,我们会发现AbstractNamedDomainObjectContainer 类configure中的方法:

    public AbstractNamedDomainObjectContainer<T> configure(Closure configureClosure) {
        ConfigureDelegate delegate = createConfigureDelegate(configureClosure);
        ConfigureUtil.configureSelf(configureClosure, this, delegate);
        return this;
    }

SourceSetContainer是一个接口,实现类继承自AbstractNamedDomainObjectContainer...这是正确的configure方法)

其中ConfigureUtil具有以下代码:

    public static <T> T configureSelf(@Nullable Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
        if (configureClosure == null) {
            return target;
        }

        configureTarget(configureClosure, target, closureDelegate);
        return target;
    }

    private static <T> void configureTarget(Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
        if (!(configureClosure instanceof GeneratedClosure)) {
            new ClosureBackedAction<T>(configureClosure, Closure.DELEGATE_FIRST, false).execute(target);
            return;
        }

        // Hackery to make closure execution faster, by short-circuiting the expensive property and method lookup on Closure
        Closure withNewOwner = configureClosure.rehydrate(target, closureDelegate, configureClosure.getThisObject());
        new ClosureBackedAction<T>(withNewOwner, Closure.OWNER_ONLY, false).execute(target);
    }

其中相关部​​分是对groovy Closure rehydrate方法的调用,根据文档执行以下操作:

Returns a copy of this closure for which the delegate, owner and thisObject are replaced with the supplied parameters. Use this when you want to rehydrate a closure which has been made serializable thanks to the dehydrate() method.

只有在configureTarget方法的最后一行,gradle 才会调用execute为表示闭包而创建的动作。所以闭包的执行是在根据 gradle 需要配置了 owner、delegate 和 this 指针之后完成的。

于 2021-01-20T16:41:26.453 回答