Gradle学习系列之读懂Gradle语法
Gradle是一种声明式的构建工具。在执行时,Gradle并不会一开始便顺序执行build.gradle文件中的内容,而是分为两个阶段,第一个阶段是配置阶段,然后才是实际的执行阶段。在配置阶段,Gradle将读取所有build.gradle文件的所有内容来配置Project和Task等,比如设置Project和Task的Property,处理Task之间的依赖关系等。
虽然很多时候我们只需要照着网上的例子写自己的DSL语句就行了,但是此时我们所知道的也就只有这么多了。如果我们能够了解Gradle DSL的内部工作机制,那么我们便可以达到举一反三的效果。在前面的文章中我们讲到,Gradle的DSL只是Groovy语言的内部DSL,也即必须遵循Groovy的语法规则。现在,让我们先来看看以下非常简单的Task:

task showDescription1 << { description = 'this is task showDescription' println description } task showDescription2 << { println description } showDescription2.description = 'this is task showDescription' task showDescription3 << { println description } showDescription3 { description = 'this is task showDescription' }

以上3个Task完成的功能均相同,即先设置Task的description属性,在将其输出到命令行。但是,他们对description的设置方式是不同的。对于showDescription1,我们在定义一个Task的同时便设置description;对于showDescription2,其本身便是Project的一个Property;而对于showDescription3,我们是在一个和它同名的方法中设置description。
事实上,对于每一个Task,Gradle都会在Project中创建一个同名的Property,所以我们可以将该Task当作Property来访问,showDescription2便是这种情况。另外,Gradle还会创建一个同名的方法,该方法接受一个闭包,我们可以使用该方法来配置Task,showDescription3便是这种情况。
要读懂Gradle,我们首先需要了解Groovy语言中的两个概念,一个Groovy中的Bean概念,一个是Groovy闭包的delegate机制。
Groovy中的Bean和Java中的Bean有一个很大的不同,即Groovy为每一个字段都会自动生成getter和setter,并且我们可以通过像访问字段本身一样调用getter和setter,比如:

class GroovyBeanExample { private String name } def bean = new GroovyBeanExample() bean.name = 'this is name' println bean.name

我们看到,GroovyBeanExample只定义了一个私有的name属性,并没有getter和setter。但是在使用时,我们可以直接对name进行访问,无论时读还是写。事实上,我们并不是在直接访问name属性,当我们执行"bean.name = 'this is name'"时,我们实际调用的是"bean.setName('this is name')",而在调用"println bean.name"时,我们实际调用的是"println bean.getName()"。这里的原因在于,Groovy动态地为name创建了getter和setter,采用像直接访问的方式的目的是为了增加代码的可读性,使它更加自然,而在内部,Groovy依然是在调用setter和getter方法。这样,我们便可以理解上面对showDescription2的description设置原理。
另外,Gradle大量地使用了Groovy闭包的delegate机制。简单来说,delegate机制可以使我们将一个闭包中的执行代码的作用对象设置成任意其他对象。比如:

class Child { private String name } class Parent { Child child = new Child(); void configChild(Closure c) { c.delegate = child c.setResolveStrategy Closure.DELEGATE_FIRST c() } } def parent = new Parent() parent.configChild { name = "child name" } println parent.child.name

在上面的例子中,当我们调用configChild()方法时,我们并没有指出name属性是属于Child的,但是它的确是在设置Child的name属性。事实上光从该方法的调用中,我们根本不知道name是属于哪个对象的,你可能会认为它是属于Parent的。真实情况是,在默认情况下,name的确被认为是属于Parent的,但是我们在configChild()方法的定义中做了手脚,使其不再访问Parent中的name(Parent也没有name属性),而是Child的name。在configChild()方法中,我们将该方法接受的闭包的delegate设置成了child,然后将该闭包的ResolveStrategy设置成了DELEGATE_FIRST。这样,在调用configChild()时,所跟闭包中代码被代理到了child上,即这些代码实际上是在child上执行的。此外,闭包的ResolveStrategy在默认情况下是OWNER_FIRST,即它会先查找闭包的owner(这里即parent),如果owner存在,则在owner上执行闭包中的代码。这里我们将其设置成了DELEGATE_FIRST,即该闭包会首先查找delegate(本例中即child),如果找到,该闭包便会在delegate上执行。对于上面的showDescription3,便是这种情况。当然,实际情况会稍微复杂一点,比如showDescription3()方法会在内部调用showDescription3的configure()方法,再在configure()方法中执行闭包中的代码。
你可能会发现,在使用Gradle时,我们并没有像上面的parent.configChild()一样指明方法调用的对象,而是在build.gradle文件中直接调用task(),apply()和configuration()方法等,这是因为在没有说明调用对象的情况下,Gradle会自动将调用对象设置成当前Project。比如调用apply()方法和调用project.apply()方法的效果是一样的。查查Gradle的Project文档,你会发现这些方法都是Project类的方法。
另外举个例子,对于configurations()方法(它的作用我们将在后面的文章中讲到),该方法实际上会将所跟闭包的delegate设置成ConfigurationContainer,然后在该ConfigurationContainer上执行闭包中的代码。再比如,dependencies()方法,该方法会将所跟闭包的delegate设置成DependencyHandler。
还有,Project还定义了configure(Object object,Closure configureClosure)方法,该方法是专门用来配置对象的(比如Task),它会将configureClosure的delegate设置成object,之后configureClosure中的执行代码其实是在object上执行的。和Groovy Bean一样,delegate机制的一个好处是可以增加所创建DSL的可读性。
Gradle学习系列之读懂Gradle语法的更多相关文章
- Gradle学习系列之三——读懂Gradle语法
在本系列的上篇文章中,我们讲到了创建Task的多种方法,在本篇文章中,我们将学习如何读懂Gradle. 请通过以下方式下载本系列文章的Github示例代码: git clone https://git ...
- Gradle学习系列之一——Gradle快速入门
这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...
- Gradle学习系列之一——Gradle快速入门(转)
这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...
- Gradle学习系列之四——增量式构建
在本系列的上篇文章中,我们讲到了如何读懂Gradle的语法,在本篇文章中,我们将讲到增量式地构建项目. 请通过以下方式下载本系列文章的Github示例代码: git clone https://git ...
- Gradle学习系列之二——创建Task的多种方法
在本系列的上篇文章中,我们讲到了Gradle入门,在本篇文章中我们将讲到创建Task的多种方法. 请通过以下方式下载本系列文章的Github示例代码: git clone https://github ...
- Gradle学习系列(一)
今天就开始学习Gradle构建了,听说很牛X.本篇内容就带领我初步窥探Gradle的世界. 1.什么是Gradle 相信之前都接触过用Ant或者Meavn进行项目的构建,两者各有千 ...
- 读懂 Gradle 的 DSL
现在 Android 开发免不了要和 Gradle 打交道,所有的 Android 开发肯定都知道这么在 build.gradle 中添加依赖,或者添加配置批量打包,但是真正理解这些脚本的人恐怕很少. ...
- Gradle学习总结——抓重点学Gradle
前言 网上关于Gradle的教程很多,但很多都是以"面"切入- 通过大量讲解其用法及其API分类来阐述.但Gradle API使用技巧众多,API更是成千上百,臣妾记不住呀.个人深 ...
- Gradle学习系列之九——自定义Task类型
在本系列的上篇文章中,我们学习了多Project构建,在本篇文章中,我们将学到如何自定义Task类型. 请通过以下方式下载本系列文章的Github示例代码: git clone https://git ...
随机推荐
- C/S和B/S架构
1.C/S架构(Client/Server结构,熟知的客户机和服务器结构),它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统 ...
- 关于php条形码生成(barcode),修改样式
今天听错了需求,以为要重新设计条形码,第一次制作这个,经过搜索使用的barcode这个第三方的,具体使用步骤网上很多就不在这里详细介绍了.主要是今天遇到的样式修改问题: barcode经过查看是无法自 ...
- 287. Find the Duplicate Number 找出数组中的重复数字
[抄题]: Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive ...
- Robotics Tools
https://sites.google.com/site/sunglok/rv_tool/robot Robotics Tools Contents 1 Robotics Tutorials 2 R ...
- exchange 2010 数据库管理
1. 查看数据库中空白空间 Get-MailboxDatabase databasename -Status | FL AvailableNewMailboxSpace 2.卸载数据库 Dismoun ...
- Zookeeper系列2 原生API 以及核心特性watcher
原生API 增删改查询 public class ZkBaseTest { static final String CONNECT_ADDR = "192.168.0.120"; ...
- Maven学习 二 Maven环境搭建
第一步:下载Maven并解压 注意选择镜像地址,选择国内的会快点 解压目录 Maven目录分析 bin:含有mvn运行的脚本 boot:含有plexus-classworlds类加载器框架 conf: ...
- SpringMVC学习 十三 拦截器栈
拦截器栈:就是有多个拦截器同时拦截相同的控制器(controller)请求,这写拦截器就构成了拦截器栈. 栈的特点是先进后出,在拦截器栈中也是如此,如果先执行了preHandle方法,也就是意味着先进 ...
- (22)Embrace the near win
https://www.ted.com/talks/sarah_lewis_embrace_the_near_win/transcript?referrer=playlist-talks_to_get ...
- Programming | 中/ 英文词频统计(MATLAB实现)
一.英文词频统计 英文词频统计很简单,只需借助split断句,再统计即可. 完整MATLAB代码: function wordcount %思路:中文词频统计涉及到对"词语"的判断 ...