Groovy是基于JVM虚拟机的一种动态语言,它的语法和Java非常的相似,Groovy完全兼容Java,又在此基础上增加了很多动态类型和灵活的特性,比如支持闭包,支持DSL,是一门非常灵活的动态脚本语言。换句话说就是让你像写Java一样编写配置脚本。
每个gradle的build脚本文件都是一个Groovy脚本文件,你可以在里面写任何符合Groovy语法的代码,比如定义类、声明函数、定义变量等。而Groovy又完全兼容Java,这就意味着你可以在build脚本文件里写任何的Java代码。
1、字符串
在Groovy中,分号不是必需的。
在Groovy中,单引号和双引号都可以定义一个字符串常量。区别:单引号 - 定义的是一个纯粹的字符串常量;双引号 - 可以对字符串里面的表达式做运算,比如:
task printlnStrVar {
def name = "json"
doLast{
print '单引号变量的计算结果:${name}'
print "单引号变量的计算结果:${name}"
}
}
-> 运行结果:
单引号变量的计算结果:${name}
单引号变量的计算结果:json
即单引号字符串没有运算能力。
2、集合
Groovy完全兼容Java的集合,并进行了扩展。常见的集合有List、Set、Map和Queue。
2.1.1、List
定义List:def numList = [1,2,3,4,5,6],numList是一个ArrayList类型;
元素访问 - 直接通过索引的方式访问List中的元素。
numList[1] 访问第二个元素
numList[-1] 访问最后一个
numList[-2] 访问倒数第二个
numList[1..3] 访问第二个到第四个元素
元素遍历 - each函数,接受一个闭包作为参数,可以访问集合中每一个元素;
task eachList {
doLast {
def numList = [1, 2, 3, 4, 5]
numList.each {
println it
}
}
}
2.1.2、Map
Map和List类似,只不过它的值是一个K:V键值对。
task map {
doLast {
def map = ['width': 1024, 'height': 766]
map.each {//被迭代的元素是Map.entry
println "Key:${it.key},Value:${it.value}"
}
}
}
执行结果:
Key:width,Value:1024
Key:height,Value:766
对于集合,Groovy还提供了诸如find、collect、findAll等便捷方法。
3、方法
Gradle脚本里面的很多代码,其实是->方法调用。
3.1.1、括号可省略
task invokeMethod {
doLast {
method1(1, 2)
method1 1, 2
}
}
def method1(int a0, int a1) {
print a0 + a1
}
3.1.2、return语句可以不写
在Groovy中,return语句不是必须的,当没有return,Groovy会把最后一句代码最为返回值。
3.1.3、代码块可以作为参数传递
代码块 - 一段被花括号包围的代码,Groovy允许其作为参数传递。 - 闭包。
map.each({
println "Key:${it.key},Value:${it.value}"
})
map.each() {
println "Key:${it.key},Value:${it.value}"
}
map.each {//被迭代的元素是Map.entry
println "Key:${it.key},Value:${it.value}"
}
以上三种方式是 等价的。
3.1.4、JavaBean
我们知道在Java中定义一个JavaBean并访问各个属性是非常繁琐的,需要一大堆的getter/setter方法。但是在groovy中很简洁,无需getter/setter方法,因为这些groovy帮我们搞定了。另外,在groovy中定义属性不一定要定义成员变量,直接写getter/setter方法一样可以作为属性访问。
task accessBean {
doLast {
Bean bean = new Bean()
bean.name = "json"
print "name的值是${bean.name}"
print "age的值是${bean.age}"
}
}
class Bean {
private String name
public int getAge(){
12
}
}
name的值是json
age的值是12
4、闭包
4.1.1、初识闭包
闭包其实就是一段代码段,是Groovy支持的一个非常重要的属性,闭包可以说是DSL的基础,它使得代码灵活轻量,再也不用像Java一样动不动就要用一个类了。
下面我们一步步实现自己的闭包,了解闭包it变量的由来。
task eachTask {
doLast {
customEach ({
println(it)
})
或者
customEach {
println(it)
}
}
}
def customEach(Closure closure) {
for (int i in 1..10) {
closure(i)//执行闭包
}
}
上面的例子中,我们定义了一个customEach方法,该方法接收一个闭包(代码块)作为参数。闭包是如何执行的呢?很简单,closure跟一对括号就是执行了,把它当作方法的调用,括号里的参数就是该闭包接收的参数。
4.1.2、向闭包传递参数
当闭包只有一个参数得时候,默认就是it;当有多个参数,it就不能表示了。
task helloClosure {
doLast {
eachMap { k, v ->
println "key is ${k} ,value is ${v}"
}
}
}
def eachMap(Closure closure) {
def map1 = ["key1": "value1", "key2": "value2"]
map1.each {
closure(it.key, it.value)
}
}
我们为闭包传递了两个参数,一个是key,一个是value,这是我们就不能用it了,必须显式的声名。
4.1.3、闭包委托
Groovy的强大之处在于它支持闭包方法的委托。Groovy的闭包有thisObject、owner、delegate三个属性。当你在闭包内调用方法时,由它们确定使用哪个对象来处理。默认情况下,owner和delegate是相等的,但是delegate是可以被修改的,这个功能很强大,Gradle中的闭包的很多功能都是通过修改delegate实现的。
task helloDelegate {
doLast {
new Delegate().test {
println "thisObject:${thisObject.getClass()}"
println "owner:${owner.getClass()}"
println "delegate:${delegate.getClass()}"
method1()
it.method1()
}
}
}
def method1() {
println "Context is : ${this.getClass()} in root"
println "method in root"
}
class Delegate {
def method1() {
println "Context is : ${this.getClass()} in delegate"
println "method in delegate"
}
def test(Closure<Delegate> closure) {
closure(this)
}
}
-----执行结果-----
thisObject:class build_ds3l6u48uknr595fy7ohwc6cz
owner:class build_ds3l6u48uknr595fy7ohwc6cz$_run_closure4$_closure9
delegate:class build_ds3l6u48uknr595fy7ohwc6cz$_run_closure4$_closure9
Context is : class build_ds3l6u48uknr595fy7ohwc6cz in root
method in root
Context is : class Delegate in delegate
method in delegate
通过上面的例子,我们发现thisObject优先级最高,默认情况下,优先使用thisObject来处理闭包中调用的方法,如果有,则执行。从输出可以看出,thisObject其实是这个构建脚本的上下文,它和脚本中的this是相等的。
从例子中也证明了delegate和owner是相等的,它们的优先级是:owner高于delegate。所以闭包内方法的处理顺序是:thisObject > owner > delegate。
在DSL中,比如Gradle,我们一般会指定delegate为当前的it,这样就可以在闭包中对it进行配置了,或者调用其方法。
class Person {
private String name
private int age
def dumpPerson() {
println "name is ${name}, age is ${age}"
}
}
def person(Closure<Person> closure) {
Person p = new Person()
closure.delegate = p
closure.setResolveStrategy(Closure.DELEGATE_FIRST)
closure(p)
}
task dump {
doLast {
person {
name = "json"
age = 10
dumpPerson()
}
}
}
例子中,我们设置了委托对象为当前创建的对象Person实例,并且设置了委托模式优先,所以我们使用person方法创建一个Person实例的时候,可以在闭包中直接对该Person实例配置。
闭包委托的本质相当于为当前闭包传递了委托对象作为隐式默认参数。
有没有发现和我们在Gradle中使用task创建一个Task的用法很像,其实gradle中有很多类似的用法。在gradle中也基本上都是使用delegate的方式使用闭包进行配置等操作。
5、DSL
DSL:特定领域语言,在于专,不在于全。
Gradle就是一门DSL,它基于Groovy,专门解决自动化构建的DSL。我们只需按照Gradle DSL定义的语法就可以达到自动化构建的目的。