这些 Groovy 闭包在 Gradle 构建脚本中仅包含标识符是什么?

What are these Groovy closures containing only identifiers in Gradle build scripts?

提问人:Sebastian Oberhoff 提问时间:3/17/2023 更新时间:3/18/2023 访问量:79

问:

阅读 Gradle 构建脚本时,我不断遇到诸如

configurations {
  myConfig
}

我知道这段代码调用了带有闭包的函数。但那是什么闭合体呢?它只是引用了一些未定义的东西,称为 .这个闭包在运行时有什么作用?毕竟,以下代码configurations{ myConfig }myConfig

def myClosure = { foo }
myClosure()

可预见的产量.Caught: groovy.lang.MissingPropertyException: No such property: foo for class: Main

Gradle Groovy 开合

评论


答:

1赞 chubbsondubs 3/17/2023 #1

省略括号时,Groovy 看起来真的很奇怪。加入闭包委托和元编程,事情可能与其他语言大不相同。Gradle 是声明式配置和在 DSL 代码包装器中实现的命令式代码的混合体,因此“我正在配置这个”和“我正在编写代码来执行此操作”之间的界限肯定是模糊的。归根结底,它只是在运行代码,但这是从“这一切都只是代码”的角度思考它会让你去“Wat!?

该块是一个 ConfigurationContainer,容器是非常灵活的实体,其中的元素可以是您想要的任何元素。也许如果我们这样重写它,它会不那么奇怪:configurations

configurations {
   dev {
   }
   staging {
   }
   production {
   }
}

当有人不带括号时,它与空的 Closure 块相同。这意味着有效地接受默认值。 现在,我用更熟悉的更具体的示例(开发、暂存、生产)替换了 myConfig。但你可以想象,我们可能希望使配置灵活,客户可以在这里定义任何适合他/她环境的东西。另请注意,我添加了空的 Closure 块,以显示这些配置可以进一步配置以下属性:myConfig

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."
   }
}

现在,所有这些是如何在引擎盖下实现的?好吧,一旦你了解了属性在时髦的闭包中的功能,这真的非常简单。如果你正在实现我们自己,你会做这样的事情(这是松散的想法伪代码):delegateConfigurationContainer

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),但你可以这样想。

  1. 我正在使用元编程方法来捕获用户声明的任何配置名称(即开发、暂存、生产、aws、azure 等)。methodMissing
  2. 创建一个对象,该对象可能包含此块要配置的某些属性。Config
  3. 我正在抓住第一个参数(即期待一个闭包)
  4. 将 的 设置为我的对象。delegateClosureConfig
  5. 调用闭包call()

现在,对该闭包中的属性或方法的任何调用都会将其委托给该属性上设置的实例。这就像在闭合中写作而不需要说.闭合物知道你的意思。delegateconfig.debugconfig.

这意味着,如果我们在 Gradle 中做这个假设的事情,我们的代码可以变成:

configurations {
   dev {
      debug = true
   }
   staging {
      debug = true
   }
   production
}

因为我们的对象已经有一个默认值 和 production 只需要声明,但不需要配置。Configdebug = false

的概念是当你试图调用你自己时所缺少的。如果不设置属性,则 中的代码不知道如何找到这些属性并抛出 .delegateClosuredelegateClosureMisingPropertyException

2赞 Leonard Brünings 3/17/2023 #2

在Groovy中,您可以将委托分配给闭包,这些闭包是用于解析闭包中使用的属性和方法的对象。

例如,将映射分配给闭包

def map = [foo: "bar"]

def myClosure = { foo }
myClosure.delegate = map
myClosure()

这将打印 .bar

使用 Groovy Web 控制台进行尝试

委托还可以实现 methodMissingpropertyMissing 来对动态属性做出反应。