Gradle是基于Groovy的DSL基础上的构建工具,Gradle中的闭包,其原型上实际上即Groovy中闭包。而在表现形式上,其实,Gradle更多的是以约定和基于约定基础上的配置去展现。但本质上,大多数配置,实际上都对应着闭包以及闭包的具体使用。

例如,实际Android项目中,我们经常看到类似如下的所谓配置项:

allprojects {
repositories {
mavenLocal()
maven {
url 'http://maven.aliyun.com/nexus/content/groups/public/'
}
google()
jcenter()
} configurations.all {
resolutionStrategy.cacheChangingModulesFor 1, 'seconds'
}
} task clean(type: Delete) {
delete rootProject.buildDir
delete "${rootProject.rootDir}/jenkinsOut"
}
复制代码

当我们在allprojects上按住command键时,发现有如下图所示的提示。

出现的提示指的是此配置项所对应的Gradle中原型,可以点击直接进入对应的Gradle API。

/**
* <p>Configures this project and each of its sub-projects.</p>
*
* <p>This method executes the given closure against this project and its sub-projects. The target {@link Project}
* is passed to the closure as the closure's delegate.</p>
*
* @param configureClosure The closure to execute.
*/
void allprojects(Closure configureClosure);
复制代码

我们发现,我们常用的allprojects配置,实际上真正对应着的,是一个void allprojects(Closure configureClosure)Java方法,而其后{}中的配置,实际上整体是一个Closure类型的参数,在方法说明中,指出这个方法是为当前项目及其子项目执行给定的闭包,目标@Project作为闭包的委托传递给闭包

于是,到底什么是闭包,闭包具体的运作机制是怎么样的,有必要实际窥探一番。

点击Gradle API中的Closure,可以进入对应的Closure类型声明,实际上对应的是Groovy jar包中的class文件声明。

package groovy.lang;

import ...

public abstract class Closure<V> extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable<V>, Serializable {
复制代码

Closure,翻译过来是闭包,在JS等语言中也存在闭包的概念,但是,不同语言中,对于闭包的具体描述或实际的应用,不同语言,可能还有所不同。

先了解一下Groovy闭包的描述:

闭包,是一个代码块,或可以理解成一个匿名函数,在外部方法调用时,可以将其作为方法的实参传递给方法的形参,并在方法内部回调此匿名函数,且回调此匿名函数时可以传递实参给到匿名函数的内部去接收,并执行此匿名函数。
同时,此代码块或匿名函数也可以赋值给一个变量,使其具有自执行的能力,且最后一行的执行语句作为匿名函数的返回。
复制代码

看着好像不太容易理解,可以具体看几个实际例子。

// 1,定义一个闭包,赋值给一个变量,并进行显示的自我调用。
def t = {
println "Hello Closure"
}
// 此处也可以写成t.call()
t() // 运行后,输出结果为:
Hello Closure
复制代码

其中,以变量的方式调用闭包t()t.call()是等价的。

// 2,定义一个闭包,赋值给一个变量,并进行显示的自我调用,并检测其返回值
def t = {
println "Hello Closure"
"ttt"
}
println "closure return: " + t.call() // 运行后,输出结果为:
Hello Closure
closure return: ttt
复制代码
// 3,定义一个闭包,赋值给一个变量,并进行显示的自我调用,调用时向闭包传递实参
def t = {
println "Hello Closure, the param value is: " + it
} t("mm") // 运行后,输出结果为:
Hello Closure, the param value is: mm
复制代码

调用闭包时,如果向闭包传递实参,闭包内部如果没有声明形参接收,默认是以it的变量的一个形参去接收实参。

因此,例3实际上是等价于:

def t = {
it ->
println "Hello Closure, the param value is: " + it
} t("mm")
复制代码
// 4,如果闭包中显示的声明了形参,则以显示的声明的形参去接收实参
def t = {
x, y ->
println "Hello Closure, the param value is: " + x + ", " + y
} t("mm", "nn") // 运行后,输出结果为:
Hello Closure, the param value is: mm, nn
复制代码

以上,都是将闭包赋值给一个变量后,进行的闭包的调用行为。

同时,我们也可以将闭包作为一个方法实参,在方法调用时传递给方法形参,然后方法内部形成对此闭包的回调。

// 5,将闭包作为一个方法实参,在方法调用时传递给方法形参,然后方法内部形成对此闭包的回调
class Person { String getName(Closure closure) {
closure("cc", "dd")
}
} def t = {
x, y ->
println "Hello Closure, the param value is: " + x + ", " + y
} new Person().getName(t) // 运行后,输出结果为:
Hello Closure, the param value is: cc, dd
复制代码

例5中的闭包如果没有事先赋值给变量t,而也可以直接使用,效果等价于:

class Person {

    String getName(Closure closure) {
closure("cc", "dd")
}
} new Person().getName({
x, y ->
println "Hello Closure, the param value is: " + x + ", " + y
})
复制代码

闭包作为方法中的最后一个参数,可以从()中拿出来,则等价于:

new Person().getName(){
x, y ->
println "Hello Closure, the param value is: " + x + ", " + y
}
复制代码

同时,方法后的()可以去掉,则等价于:

new Person().getName {
x, y ->
println "Hello Closure, the param value is: " + x + ", " + y
}
复制代码

如果外部调用闭包的方法传递实参时,没有传递实参或只传递了一个参数(如果没有传递实参,则it为null),则进一步演化成:

class Person {

    String getName(Closure closure) {
closure("cc")
}
} new Person().getName {
println "Hello Closure, the param value is: " + it
}
复制代码

这也就是我们在Gradle中经常见到的闭包形式,即表面上只有{}的配置形式。

将闭包理解成一个特殊的匿名函数,无论是通过变量的自调用,还是作为方法实参的传递后,在方法内部被回调,闭包的最后一行执行被当做匿名函数的整体返回,都可以很好的得以理解。同时,也能容易的理解闭包可以嵌套使用等(即当做匿名函数的嵌套)。

如:以Gradle中可能经常见到的each写法为例:

dirs.each { dir ->
java.srcDir("src/$dir/java")
res.srcDir("src/$dir/res")
}
复制代码

实际上内部对应的执行过程为:

public static <T> List<T> each(List<T> self, @ClosureParams(FirstGenericType.class) Closure closure) {
return (List)each((Iterable)self, closure);
} public static <T> Iterable<T> each(Iterable<T> self, @ClosureParams(FirstGenericType.class) Closure closure) {
each(self.iterator(), closure);
return self;
} public static <T> Iterator<T> each(Iterator<T> self, @ClosureParams(FirstGenericType.class) Closure closure) {
while(self.hasNext()) {
Object arg = self.next();
closure.call(arg);
} return self;
}
复制代码

显然,内部最终通过closure.call(arg)回调了闭包自身,并向闭包传递了实参。

闭包在Gradle中的配置中,被大量使用。理解Gradle中的闭包,对一些特殊的写法,如Gradle构建生命周期中的闭包回调中的实参使用等,可以有很好的运用。

如常见的在Gradle构建的配置阶段中的afterEvaluate hook中,可以设置相关task的依赖关系等。此时,it接收的是回传进来的当前project实参。

afterEvaluate {
... Task assembleJenkinsTask = rootProject.tasks.getByName('assembleJenkins') Task unitTestTask = it.tasks.findByName('testDebugUnitTest') if (unitTestTask != null) {
assembleJenkinsTask.dependsOn unitTestTask } ...
}
复制代码

在一定意义上,Groovy中闭包的概念,以及其实际的用法上,实质上根Java 8中的lambda表达式具有很相近的含义。

作者:HappyCorn
链接:https://juejin.im/post/5c4af28be51d4511dc72fcca
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Gradle中的闭包的更多相关文章

  1. Gradle学习之闭包

    Gradle中的闭包其实就等同于Groovy中闭包,Groovy是一种jvm语言,语法兼容于java,曾几何时,也在脚本语言中独树一帜,初学Gradle的时候,大家很容易被其语法所迷惑,由于Gradl ...

  2. Gradle 1.12 翻译——第十七章. 从 Gradle 中调用 Ant

    有关其他已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或访问:http://gradledoc.qiniudn.com ...

  3. Groovy中的闭包

    Closures(闭包) 本节主要讲groovy中的一个核心语法:closurs,也叫闭包.闭包在groovy中是一个处于代码上下文中的开放的,匿名代码块.它可以访问到其外部的变量或方法. 1. 句法 ...

  4. gradle中使用嵌入式(embedded) tomcat, debug 启动

    在gradle项目中使用embedded tomcat. 最开始部署项目需要手动将web项目打成war包,然后手动上传到tomcat的webapp下,然后启动tomcat来部署项目.这种手动工作通常还 ...

  5. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  6. 【原】理解javascript中的闭包

    闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见 ...

  7. 难道这就是JavaScript中的"闭包"

    其实对于JavaScript中的"闭包"还没真正理解,这次在实际Coding中似乎遇到了"闭包"的问题,仅此摘录,以待深究. 表现为jQuery的post方法回 ...

  8. 【原】如何在jQuery中实现闭包

    原生JS中,闭包虽好用,但是很难用好,在jQuery中一样,都有一些点需要我们注意.jQuery中使用闭包的常见情况有以下几种: 1.$(document).ready()的参数 我们在写jQuery ...

  9. 说说Python中的闭包 - Closure

    转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西 ...

随机推荐

  1. 代码质量管理平台SonarQube的安装、配置与使用

    SonarQube是管理代码质量一个开放平台,可以快速的定位代码中潜在的或者明显的错误,下面将会介绍一下这个工具的安装.配置以及使用. 准备工作: 1.jdk(不再介绍) 2.sonarqube:ht ...

  2. android sqlite android.database.CursorIndexOutOfBoundsException: Index 5 requested, with a size of 5

    Cursor c = db.query("user",null,null,null,null,null,null);//查询并获得游标 if(c.moveToFirst()){// ...

  3. 前端leader找我谈心:我是如何从刚毕业的前端菜鸟一步步成长为前端架构师的?

    谈谈学习 我做前端已经有五年的时间了,从大学刚毕业的时候,我是一个完全什么都不懂的小白.虽然我大学里学的是软件工程专业,但是因为在大学里荒废学业,每天只知道打游戏,基本上到大学毕业之前我是什么都不会的 ...

  4. thinkphp 自动生成模块目录结构

    要达到的目的 在application目录下创建自定义模块如admin,用命令行方式自动创建该目录及目录下默认结构 要运行的命令 > php think build --module admin ...

  5. 2017年的golang、python、php、c++、c、java、Nodejs性能对比[续]

    2017年的golang.python.php.c++.c.java.Nodejs性能对比[续] 最近忙,这个话题放了几天,今天来个续集.   上篇传送门: 2017年的golang.python.p ...

  6. [ Java面试题 ]持久层篇

    1.什么是ORM?  对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术: 简单的说,ORM是通过使用描 ...

  7. Ueditor 专题

    https://github.com/xwjie/SpringBootUeditor 提交表单提交表单设置按照部署编辑器的教程,完成编辑器加载 把容器放到form表单里面,设置好要提交的路径,如下面代 ...

  8. 唱吧DevOps的落地,微服务CI/CD的范本技术解读

    1.业务架构:从单体式到微服务 K歌亭是唱吧的一条新业务线,旨在提供线下便捷的快餐式K歌方式,用户可以在一个电话亭大小的空间里完成K歌体验.K歌亭在客户端有VOD.微信和Web共三个交互入口,业务复杂 ...

  9. 你不知道的JavaScript--Item16 for 循环和for...in 循环的那点事儿

    大家都知道在JavaScript中提供了两种方式迭代对象: for 循环: for..in循环: 1.for循环 不足: 在于每次循环的时候数组的长度都要去获取: 终止条件要明确: 在for循环中,你 ...

  10. Android 深入理解Android中的自定义属性

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/45022631: 本文出自:[张鸿洋的博客] 1.引言 对于自定义属性,大家肯定 ...