阅读 Gradle 构建脚本我不断遇到这样的代码
configurations {
myConfig
}
我明白这段代码用闭包
configurations
调用函数{ myConfig }
。但那是什么样的闭包体呢?它只是引用了一些未定义的东西,叫做myConfig
。这个闭包在运行时是如何做的?毕竟下面的代码
def myClosure = { foo }
myClosure()
可预测的产量
Caught: groovy.lang.MissingPropertyException: No such property: foo for class: Main
.
在 Groovy 中,您可以将 delegates 分配给闭包,这些对象将用于解析闭包中使用的属性和方法。
一个例子是将一个映射分配给闭包
def map = [foo: "bar"]
def myClosure = { foo }
myClosure.delegate = map
myClosure()
这将打印
foo
.
Delegates 也可以实现 methodMissing 或 propertyMissing 来对动态属性做出反应。
Groovy 在省略括号时看起来真的很奇怪。加上闭包委托和元编程,事情可能与其他语言大不相同。 Gradle 混合了声明式配置和在 DSL 代码包装器中实现的命令式代码,因此“我正在配置这个”与“我正在编写代码来执行此操作”之间的界限肯定是模糊的。归根结底,它只是在运行代码,但这是从“这只是代码”的角度思考它的时代之一,会让你“哇!?”
configurations
块是一个ConfigurationContainer,容器是非常灵活的实体,其中的元素可以是任何你想要的。也许如果我们像这样重写它会不那么奇怪:
configurations {
dev {
}
staging {
}
production {
}
}
当有人写
myConfig
时没有括号,它与一个空的 Closure 块相同。这将意味着有效地接受默认值。
现在,我将 myConfig 替换为更熟悉、更具体的示例(开发、暂存、生产)。但是您可以想象我们可能希望使配置灵活,并且客户可以在这里定义适合他/她的环境的任何内容。另请注意,我在之后添加了空的 Closure 块以显示那些 Configurations 可以使用以下属性进一步配置:
configurations {
dev {
description = "Used for local development only."
}
staging {
description = "Can be deployed to the staging environment only."
}
production {
description = "The environment deployed to customer facing system."
}
}
现在所有这些是如何在幕后实施的?好吧,一旦您理解了
delegate
属性在 groovy 闭包中的作用,它就非常简单了。如果你自己实现ConfigurationContainer
你会做这样的事情(这是松散的想法伪代码):
public class ConfigurationContainer {
Map<String,Config> configs = [:]
def methodMissing(String name, def args) {
configs[ name ] = new Config( name )
Closure c = args.first()
c.delegate = configs[ name ]
c.call()
}
}
class Config {
boolean debug = false
}
现在,如果您查看 Groovy 代码(如果您想创建类似的东西,请参阅 NamedDomainObjectContainer),这并不是它的完成方式,但您可以这样想。
methodMissing
来捕获用户声明的任何配置名称(即 dev、staging、production、aws、azure 等)。Config
对象,该对象可能包含此块想要配置的一些属性。delegate
的Closure
设置为我的Config
对象。call()
现在,对该闭包中的属性或方法的任何调用都将delegate 到
delegate
属性上设置的实例。这就像在闭包中写config.debug
而不需要说config.
。闭包知道你的意思。
这意味着如果我们在 Gradle 中做这个假设的事情,我们的代码可能会变成:
configurations {
dev {
debug = true
}
staging {
debug = true
}
production
}
因为我们的
Config
对象已经有默认的 debug = false
并且生产只需要声明,但不需要配置。
delegate
的概念是您尝试自己调用 Closure
时所缺少的。如果不设置 delegate
属性,Closure
中的代码不知道如何找到这些属性并抛出 MisingPropertyException
.