Gradle学习系列之读懂Gradle语法

转载地址:
http://www.cnblogs.com/CloudTeng/p/3418072.html

 


   Gradle是一种声明式的构建工具。在执行时,Gradle并不会一开始便顺序执行build.gradle文件中的内容,而是分为两个阶段,第一个阶段是配置阶段,而后才是实际的执行阶段。在配置阶段,Gradle将读取全部build.gradle文件的全部内容来配置Project和Task等,好比设置Project和Task的Property,处理Task之间的依赖关系等。html

  虽然不少时候咱们只须要照着网上的例子写本身的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。app

  事实上,对于每个Task,Gradle都会在Project中建立一个同名的Property,因此咱们能够将该Task看成Property来访问,showDescription2即是这种状况。另外,Gradle还会建立一个同名的方法,该方法接受一个闭包,咱们可使用该方法来配置Task,showDescription3即是这种状况。工具

  要读懂Gradle,咱们首先须要了解Groovy语言中的两个概念,一个Groovy中的Bean概念,一个是Groovy闭包的delegate机制。gradle

  Groovy中的Bean和Java中的Bean有一个很大的不一样,即Groovy为每个字段都会自动生成getter和setter,而且咱们能够经过像访问字段自己同样调用getter和setter,好比:ui

复制代码
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设置原理。this

  另外,Gradle大量地使用了Groovy闭包的delegate机制。简单来讲,delegate机制可使咱们将一个闭包中的执行代码的做用对象设置成任意其余对象。好比:spa

复制代码
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的可读性。