在将闭包传递给 gradle 扩展时,为什么闭包的所有者不是主要的 Projects 对象?
On passing a closure to a gradle extension, why the owner of closure is not the main Projects object?
我正在查看闭包作用域并发现输出计数器很直观,这段代码在 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 项目名称。
TL;DR;
基本上在 gradle 源代码中有一个类似这样的方法:
public Object sourceSets(Closure closure) {
// do stuff like configuring the closure
closure.call()
}
所以当你打电话时:
sourceSets {
some code
}
(顺便说一句,这与调用 sourceSets({ some code })
相同,只是删除了括号,这在 groovy 中是可以的)
调用sourceSets
方法时,“某些代码”不会立即执行。 Gradle 可以选择在他们决定的时间执行它。具体来说,他们可以(并且确实)在实际执行闭包之前配置所有者和委托之类的东西。
更长的版本
原来 build.gradle
文件中的 sourceSets
方法实际上是由插件添加的,例如 java/kotlin/groovy 插件。
作为示例,我们可以查看 java 插件和具有以下代码的 DefaultJavaPluginConvention class:
private final SourceSetContainer sourceSets;
@Override
public Object sourceSets(Closure closure) {
return sourceSets.configure(closure);
}
这是当您在 build.gradle
文件中键入 sourceSets { ... }
时调用的方法。它获得闭包并继续将其交给源集容器的 configure
方法。请注意,我们还没有执行闭包,我们只是将其作为未执行的代码块传递。
如果我们稍微挖掘一下,我们会在 AbstractNamedDomainObjectContainer class:
中找到 configure
方法
public AbstractNamedDomainObjectContainer<T> configure(Closure configureClosure) {
ConfigureDelegate delegate = createConfigureDelegate(configureClosure);
ConfigureUtil.configureSelf(configureClosure, this, delegate);
return this;
}
(SourceSetContainer
是一个接口,实现 class 继承自 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
对创建的表示闭包的操作。所以闭包的执行是在owner、delegate和this指针根据gradle需要配置完成后执行的。
我正在查看闭包作用域并发现输出计数器很直观,这段代码在 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 项目名称。
TL;DR;
基本上在 gradle 源代码中有一个类似这样的方法:
public Object sourceSets(Closure closure) {
// do stuff like configuring the closure
closure.call()
}
所以当你打电话时:
sourceSets {
some code
}
(顺便说一句,这与调用 sourceSets({ some code })
相同,只是删除了括号,这在 groovy 中是可以的)
调用sourceSets
方法时,“某些代码”不会立即执行。 Gradle 可以选择在他们决定的时间执行它。具体来说,他们可以(并且确实)在实际执行闭包之前配置所有者和委托之类的东西。
更长的版本
原来 build.gradle
文件中的 sourceSets
方法实际上是由插件添加的,例如 java/kotlin/groovy 插件。
作为示例,我们可以查看 java 插件和具有以下代码的 DefaultJavaPluginConvention class:
private final SourceSetContainer sourceSets;
@Override
public Object sourceSets(Closure closure) {
return sourceSets.configure(closure);
}
这是当您在 build.gradle
文件中键入 sourceSets { ... }
时调用的方法。它获得闭包并继续将其交给源集容器的 configure
方法。请注意,我们还没有执行闭包,我们只是将其作为未执行的代码块传递。
如果我们稍微挖掘一下,我们会在 AbstractNamedDomainObjectContainer class:
中找到configure
方法
public AbstractNamedDomainObjectContainer<T> configure(Closure configureClosure) {
ConfigureDelegate delegate = createConfigureDelegate(configureClosure);
ConfigureUtil.configureSelf(configureClosure, this, delegate);
return this;
}
(SourceSetContainer
是一个接口,实现 class 继承自 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
对创建的表示闭包的操作。所以闭包的执行是在owner、delegate和this指针根据gradle需要配置完成后执行的。