Gradle的核心功能是用Java建立的。在此之上是用动态语言Groovy编写的domain specific language(DSL)。当编写一个Gradle构建脚本时,你自动地就使用了由该DSL暴露出来的语言结构来表达你的构建指令。Gradle构建脚本是可执行的Groovy脚本,但它们不能被Groovy运行时运行。当实现定制逻辑的需求出现时,你可以使用Groovy的语言特性直接在Gradle构建脚本中建立出想要的功能。本文是Groovy语言的入门读物,并解释了为什么Gradle用户学习Groovy很重要。后面,我还会说明Gradle的配置元素是如何用Groovy实现的。
1、Groovy是什么?
Groovy是一门动态编程语言。它的语法和Java的很相近。它集成了已有的Java类和库,这使得Java开发人员学习Groovy很容易。Groovy不仅是在Java的强大之上建立起来的,它还提供了受到Ruby、Python以及其他语言启发的强大编程特性。Groovy可以被用作为一个脚本语言而无需编译。Groovy代码也可以被编译成java字节码。
2、我需要知道多少Groovy知识
编写首个构建脚本的话,知道那么一点就行了。然而,强烈建议你要知道一些Java。Groovy几乎百分百兼容Java。当实现不重要的琐碎的任务,你可以选择编写Java代码或是使用Groovy的富于表现力的语言结构。
先来看一个例子。假设你想要确定某个目录中所有的文件,并将这些文件的名字写到一个新文件allfiles.txt中。听起来很简单,对吧?下面是task action doLast的Java版。
task appendFilenames << {
File inputDirectory = new File("src");
File outputFile = new File(getBuildDir(), "allfiles.txt");
File outputDirectory = outputFile.getParentFile();
if(!outputDirectory.exists()) {
outputDirectory.mkdirs();
}
outputFile.createNewFile();
FileWriter fileWriter = new FileWriter(outputFile, true);
try {
for(File file : inputDirectory.listFiles()) {
fileWriter.write(file.getName() + "\n");
}
}
finally {
fileWriter.close();
}
}
可以看出来,除了任务定义本身是用Gradle的DSL定义的,不需要任何Groovy代码。再来看看Groovy版的代码(更简短):
task appendFilenames << {
def inputDirectory = new File('src')
def outputFile = new File(buildDir, 'allfiles.txt')
def outputDirectory = outputFile.parentFile
if(!outputDirectory.exists()) {
outputDirectory.mkdirs()
}
outputFile.createNewFile()
inputDirectory.eachFile { outputFile << "$it.name\n" }
}
对于更复杂(包含定制任务以及插件)的构建,就需要知道更多的Groovy知识。
3、比较Java和Groovy的语法
用Groovy,你可以变得极其高效,同时代码量又少。
#############Java代码###############
package com.manning.gia;
public class ProjectVersion {
private Integer major;
private Integer minor;
public ProjectVersion(Integer major, Integer minor) {
this.major = major;
this.minor = minor;
}
// 省略了getter、setter方法
}
###############Groovy代码###########
package com.manning.gia
class ProjectVersion {
Integer major
Integer minor
ProjectVersion(Integer major, Integer minor) {
this.major = major
this.minor = minor
}
}
Groovy对你写的类假设了一些合理的默认值,特别是一下一些优化:
(1)表达式末尾的分号不是必需的
(2)每个类、构造器以及方法默认都是public
(3)方法体的最后一条表达式会作为返回值,意味着return语句也是可选的
(4)Groovy编译器会自动添加 getter/setter 方法
(5)类的字段(也叫properties)可以用 点 来访问,但实际上在背后调用了自动生成的getter/setter方法,你还以为这不就是好像public一样吗
(6)如果你用 == 来比较一个类的2个实例,Groovy背后自动调用 equals。
4、基本的Groovy特性
有2个工具极其有用,可以运行代码。(1)Groovy console,它提供了一个交互式用户界面可以输入并执行Groovy脚本。可以运行groovyConsole或<GROOVY_HOME>/bin里的groovyConsole.bat来启动它。
(2)Groovy web console
4.1 Assert语句
如果你来自Java,你可能知道assert这个关键字。它用于检验前置和后置条件。不过,Java中,你要开启assertion checking(通过设置运行时标识-ea或-enableassertion),这个特性才能用。Groovy的总是可用的。
def version = 12
assert version == 12
version++
assert version == 12
4.2 可选的数据类型声明
Groovy不会强制要你显式声明变量、方法参数或返回值的类型。关键字def是java.lang.Object的占位符,Groovy会根据赋的值来判断类型。
def buildTool = 'Gradle'
assert buildTool.class == java.lang.String
def initProjectVersion(major, minor) {
new ProjectVersion(major, minor)
}
assert initProjectVersion(1, 2).class == com.manning.gia.ProjectVersion
但是你也许仍然更倾向于显式、强类型。特别是在那些暴露公共API的项目中,强类型使得要提供什么类型的参数更显而易见,对IDE代码补全也有用。如果一个方法没有返回值,那用void而不是def作为返回类型。
4.3 可选的括号
如果方法签名要求至少一个参数,则方法调用可以省略括号。
initProjectVersion(1, 2)
initProjectVersion 1, 2
println('Groovy is awesome!')
println 'Groovy is awesome!'
4.4 Strings
Groovy中声明Strings有3种办法。
def myString1 = 'This is a single-quoted String'
def myString2 = "This is a double-quoted String"
def myString3 = """
This
is a
multiline
String
"""
4.5 Groovy Strings(GStrings)
Groovy中双引号括起来的字符串比传统的Java字符串更强大。插值变量或表达式。带这个的字符串叫GStrings。
def language = 'groovy'
def sentence = "$language is awesome!"
assert sentence == 'groovy is awesome!'
def improvedSentence = "${language.capitalize()} is awesome!"
assert improvedSentence == 'Groovy is awesome!'
4.6 Collections API
Groovy为集合API的实现提供了简洁的语法,比Java的更易用。
【LISTS】
方括号里放一些用逗号隔开的值就初始化了一个List。背后,Groovy创建了java.util.ArrayList的一个实例。操作符 << 可以将一个新元素添加到List中。背后,Groovy调用了add方法。
【MAPS】
Maps处理起来比Lists容易。在方括号中放一个逗号分隔的键值对就初始化了一个Map。它的默认实现是java.lang.LinkedHashMap。
def inceptionYears = ['Ant': 2000, 'Maven': 2004]
assert inceptionYears.getClass() == java.lang.LinkedHashMap
assert inceptionYears.size() == 2
assert inceptionYears.Ant == 2000
assert inceptionYears['Ant'] == 2000
inceptionYears['Gradle'] = 2009
assert inceptionYears.size() == 3
assert inceptionYears['Gradle'] == 2009
inceptionYears.each { buildTool, year ->
println "$buildTool was first released in $year"
}
4.7 Named parameters
假设没有定义构造器,Groovy提供了另一种便捷的设置属性值的办法,叫 named parameters。该机制先调用该类的默认构造器,然后再为每一个提供的参数调用对应的setter方法。
class ProjectVersion {
Integer major
Integer minor
}
ProjectVersion projectVersion = new ProjectVersion(major: 1, minor: 10)
assert projectVersion.minor == 10
projectVersion.minor = 30
assert projectVersion.minor == 30
。。。。。。。。。。。。。。。。。。。。。。。。未完。。。。。。。。。。。