任务脚本的本质
因为DSL的原因,使得Gradle的脚本看上去更像是一种声明式的语言,即便是不了解Groovy的开发人员也可以轻松的理解脚本含义。但是,如果能够明白Gradle的脚本的基本原理,能够帮助一些使用面向对象开发语言的开发人员更快速、容易的接受DSL的使用。
Gradle的脚本中万物皆对象,一切尽是API。
Gradle的任务(task)本身就是一个对象,定义task就是调用Gradle的API创建task对象。
task helloWorld << {
println 'Hello world!'
}
让我来证明一下!
证:
首先,看下面跟task相关的几个类:Project,TaskContainer和Task。
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware
public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task>
public interface Task extends Comparable<Task>, ExtensionAware
它们之间的关系是这样的:每一个task都属于一个project,你可以通过调用TaskContainer上的不同方法来创建和查询一个task实例。比如,TaskContainer.create(String)方法会根据传入的名字创建一个空的task。TaskContainer负责管理Task实例的集合。通过调用Project.getTasks()方法,可以获得TaskContainer的实例。
然后,反向推理:看那些常见的定义任务的方法以及对应的API
下面是我们经常见到的几种定义任务的方式:
task myTask
task myTask { configure closure }
task myType << { task action }
task myTask(type: SomeType)
task myTask(type: SomeType) { configure closure }
而在Gradle的API中,我们可以分别找到每一种定义方式其对应的在Project实例上的API方法:
Task task(String name)
// 根据一个给定的名字创建一个Task,并添加到对应的project中
Task task(Map<String,?> args, String name)
// 接受名字和参数
Task task(Map<String,?> args, String name, Closure configureClosure)
// 接受名字,参数和闭包
Task task(String name, Closure configureClosure)
// 接受名字和闭包
为什么是Project对象上的API?答案是代理对象
构建中的每一个project,Gradle都会创建一个Project类型的对象,并将该对象和构建脚本关联,当构建脚本执行时,它实际上是在配置Project对象(这也是为什么Gradle脚本配称为配置脚本(configuration script)):
- 任何在构建脚本中调用的方法(method),如果没有在构建脚本中定义,那么就会代理到Project对象上。
- 任何在构建脚本中访问的属性(properties),如果没有在构建脚本中定义,那么也会代理到Project对象上。
关于闭包action和左漂移符号<<
Groovy的闭包可以被用来定义一个任务的action(行为或者动作)。当action被执行,该闭包就会作为任务的一个参数被调用。可以调用task对象上的doFirst(groovy.lang.Closure)或者doLast(groovy.lang.Closure),或者左漂移符号<<来给task添加一个action的闭包。重新定义左漂移符号的含义是Groovy本身强大的特性:操作符重载。
public interface Action<T>
// 针对某种类型的T,执行对应的action
// T - 这个action可以接受的某种对象类型(泛型)
Task leftShift(Closure action)
// 将给定的闭包添加到task的action列表中
重新换个思路理解helloWorld任务,是不是更简单了!
task helloWorld << {
println 'Hello world!'
}
// 变形为类Java语法是否更好理解
task("helloWorld").leftShift({
println('hello world');
})