我在 build.gradle 中定义了一个任务,如下所示:
task copyConfig(type: Copy) {
from ""
into ""
}
在我看来,task
是project
中的一个方法,它将整个其余部分作为参数,
copyConfig
也是一个有两个参数的方法:[type:copy]
和一个闭包
{
from ""
into ""
}
但我想知道copyConfig
是自定义的,没有名为copyConfig
的方法,
那么为什么它有效呢?到底发生了什么。
最佳答案
Gradle 充分利用了 Groovy 提供的功能来创建漂亮的 DSL。
无论 Gradle 如何实现(其他答案已经指出),我们都可以使用基本的 Groovy DSL 创建工具轻松实现这一点。
例如,以下代码演示了如何使用基类评估脚本(如 Gradle 文件),这意味着脚本运行就像在我们选择的类中一样(在本例中,MyScript
):
import org.codehaus.groovy.control.CompilerConfiguration
class Main {
static void main( args ) {
def compilerConfig = new CompilerConfiguration()
compilerConfig.scriptBaseClass = 'MyScript'
def shell = new GroovyShell( this.class.classLoader, compilerConfig )
shell.evaluate( '''
task helloWorld {
println "Hello world"
}
''' )
}
}
abstract class MyScript extends Script {
def task( taskObject ) {
}
}
当我们评估这个脚本时:
task helloWorld {
println "Hello world"
}
首先,通过使用 Groovy 语言语法规则,我们可以将其转换为更类似于 Java 的语法,以了解它将执行的操作:
task(helloWorld(() -> {
System.out.println("Hello world");
}));
因此,task()
方法调用应该可以工作,因为我们在脚本的基类中定义了一个方法:def task( taskObject )
...
但是没有 helloWorld
方法,所以这应该会失败,猜猜运行它时会发生什么?
groovy.lang.MissingMethodException:
No signature of method: Script1.helloWorld() is applicable for argument types: (Script1$_run_closure1)...
我们可以看到我们对上面的 Groovy 语法的解释是正确的:Groovy 使用 Closure
的实例调用 helloWorld
!
现在,我们可以做一些聪明的事情来允许创建任何名称的任务......像这样:
abstract class MyScript extends Script {
def currentTaskName
def task( taskObject ) {
println "Trying to create task $taskObject"
}
def methodMissing( String name, def args ) {
[ name: name, config: args ]
}
}
methodMissing is another Groovy feature that lets you handle calls to any method of a class that does not actually exist!
猜猜这打印了什么!
Trying to create task [name:helloWorld, config:[Script1$_run_closure1@70325e14]]
这就是全部内容...Gradle 可能会做一些更高级的魔法,例如更改已编译的 Groovy 源代码的 AST(任何人都可以在 Groovy 中执行此操作),但本质上就是这样。
如何使用配置闭包来配置任务留给读者作为练习:P
检查Groovy DSL docs如果您想了解更多信息。
关于java - 构建 java 项目时 build.gradle 中的 Groovy 语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59087017/