Gradle是一个基于Apache Ant和Apache Maven概念的JVM项目自动化建构工具。 有别于传统的不忍卒读的XML项目设置语言,它使用基于Groovy或者Kotlin的DSL(领域专用语言)来配置项目构建流程,大大提高了可读性和易用性。Spring Framework、Hibernate等著名的开源框架都在使用Gradle,当然还包括Gradle本身。

当然Gradle本身也是有学习曲线的,很多开发者对其的理解和使用可能只停留在gradle testgradle build上。随着项目代码的增加,项目依赖也越来越多,自定义的构建步骤也开始出现,整个项目的构建速度渐渐变慢,进而使得持续集成的速度也相继变慢。

本文将基于Gradle 5.5介绍几个非常实用但鲜为人知的Gradle使用技巧,帮助读者优雅地使用Gradle。

使用Gradle Wrapper

Gradle Wrapper是官方推荐的使用Gradle的方式,因为它可以简单地声明要使用的Gradle的版本,然后在项目构建中使用那个指定的版本来跑各个构建任务。这样,不管你用的是命令行还是IDE,不管你用的是Windows还是Linux, 你用的都是同一个命令和版本,比如./gradlew build,此处./gradlew 即是所说的Gradle Wrapper。

生成Gradle Wrapper,你只需要在项目根目录下运行gradle wrapper --gradle-version 5.4.1, 你可以根据需要选择适合你的gradle-version。

--parallel

随着项目的发展,产品代码和测试代码与日俱增,然而项目的整体构建速度通常都会不断变慢。变慢的原因有很多种,比如有不可避免的编译时间增长,也有可以改善的测试运行方式。Gradle 5.x 版本中已经做了很多优化,比如默认的incremental build(增量编译)和build cache(构建缓存)。此外,现在大部分电脑都配置了多核CPU,Gradle提供了--parallel 功能来帮助你基于多核CPU并行运行Gradle的任务。比如你的项目里有三个无交叉依赖Gradle子模块,当你在一台六核的机器上跑时,三个子模块可以并行编译和运行测试,理论上你最多可以节省2/3的构建时间。

如果你想把这个选项作为人和人跑任何Gradle命令时的默认选项,你可以在你项目根目录的gradle.properties 文件里添加下面一行

org.gradle.parallel=true

--fail-fast

在CI(持续集成)构建项目时,必不可少的一个环节是跑自动化测试,比如单元测试和集成测试。大型的项目往往有成百上千个自动化测试,如果CI系统没有很好的并行化机制的话, 全部跑完至少要个五到十分钟,甚至更久。万一你的分支里有测试挂了,默认情况下你必须要等到所有测试跑完才能得到反馈(比如收到提醒)。假设那个测试是在第二十秒时就挂了,意味着我们浪费了之后五到十分钟的机器资源。

--fail-fast 正是为了解决这个问题而设计的,它的作用就是一旦有测试挂掉就直接终止,还没跑的测试就被跳过了。这样我们的CI环境资源利用率就能有所提高,对于那种有数十个甚至上百个工程师在同时工作的代码库,--fail-fast的优化效果就很明显了。

--fail-fast 只适用于Gradle Test类别的构建任务,比如 ./gradlew test --fail-fast。用于其他任务则会报错,比如./gradlew build --fail-fast,因为build不是Test类别的任务。除了用于命令行,你还可以在Gradle脚本里设置:

test {failFast = true
}

-t, --continuous

--continuous 帮助开发者在本地开发时更高效地验证他们的changes。比如你在写一个类的单元测试,你已经有以下的两个文件

# Foo.java
public class Foo {public int sum(int a, int b) {return a + b;}
}# FooTest.java
public class FooTest {@Testpublic void testSum() {// TODO: implement me}
}

你可以在命令行运行./gradlew test --tests FooTest --continuous 或者缩减版./gradlew test --tests FooTest -t,当你改变Foo.java的实现或者FooTest.java的测试代码时,Gradle会检测到文件内容改变,从而重新编译运行你的测试,无需你人工执行同样的命令。

这个功能非常适合那些需要快速反馈、迭代、验证修复的任务,比如编译、测试、重构、代码风格错误修复等。

-x, --exclude-task

当你想执行一系列Gradle构建任务但又想跳过某些很慢的或者是不相关的任务时,-x就派上用场了。比如Gradle有个任务叫check, 它往往是开发者push本地commits前跑的一个任务,可以看成一个快速代码正确性校验的综合任务,包含编译、代码静态分析、自动化测试等。如果此时你不想跑集成测试,因为集成测试很慢,你就可以在命令行跑./gradlew check -x integrationTest, 这样它就会跑除了集成测试之外的相关校验任务。

-m, --dry-run

当你修改了一些Gradle脚本,或者想快速验证你的Gradle配置是正确的,合理地应用--dry-run能帮你节省不少时间。比如你想看看check任务到底会跑哪些相关任务时,你可以用./gradlew check -m, 不出几秒Gradle就会在命令行打印出需要执行的任务名(但不执行)。

--offline

设想你在火车或者飞机上很无聊,突然灵光乍现想到一个优化你现在系统的方法,你迫不及待地打开笔记本啪啪啪啪敲完了代码,结果在命令行一跑Gradle报错说没有网络连接,无法下载一些项目依赖。再有没有比这个更让一个工程师痛苦的事了吧。其实Gradle大部分时间都在本地缓存了所有的项目依赖,只是它习惯性地会去网上重新更新校对下依赖版本等信息。此时--offline就能帮助你强制Gradle开启离线工作模式!

api / implementation / ~~compile~~

很多构建系统都会碰到一个棘手的问题:如何合理解决Transitive Dependency(依赖传递)? 比方说现在有如下依赖关系App -> Lib A -> Lib B, 如果_Lib B_ 只在_Lib A_的函数内部使用到,理想的情况下_App_的代码是不能直接调用_Lib B_的,因为_Lib B_只是_Lib A_的内部实现方式(internal implementation)。 然而大部分情况下你会发现_App_的代码竟然能调用_Lib B_

以上就是在Gradle脚本里使用compile关键词的副作用。不要以为这仅仅是一个内部实现暴露的问题,它还会造成依赖版本解决出错导致应用层出错,还会减慢项目代码的整体编译速度。

Gradle从3.4版本起其实就提供了一个解决方案。它把compile语义拆分成两类,apiimplementationapi等价于被_deprecated_的compile。至于implementation, 以上面的例子为基础

# build.gradle of App
dependencies {api project('LibA')
}# build.gradle of Lib A
dependencies {api project('models')implementation 'com.google.guava:guava:18.0'
}

这样_Guava:18_这个内部依赖就不能渗透到_App_那里。如果_App_刚好也需要使用_Guava_, 它就需要显示定义_Guava_为它的依赖,而且它可以自由选择_Guava_的版本而不用担心版本误用或者冲突。

2019.7.9

本文原载于: 【技术博客】如何优雅地使用Gradle

参考文章

  • Improving the Performance of Gradle Builds

往期回顾

娄琦彬:基于testcontainers的现代化集成测试进阶之路​zhuanlan.zhihu.com

娄琦彬:欲善其事, 必利其器之Code Quality Checks​zhuanlan.zhihu.com

艺与术公众号(various__artists): 分享关于编程、软件工程、系统架构、前沿技术的艺与术的思考。

gradle 指定springcloud 版本_如何优雅地使用Gradle相关推荐

  1. gradle 指定springcloud 版本_SpringCloud微服务架构开发实战:实现服务注册与发现

    实现服务的注册与发现 在前面分别用Eureka Server和Eureka Client来搭建了一台注册服务器,以及多个Eureka Client客户端.Eureka Client在启动后,就会将自己 ...

  2. gradle 指定springcloud 版本_Gradle初探

    1.安装gradle brew install gradle 2.Hello World 1.创建构建脚本文件:build.gradle 2.编写构建脚本: task hello{ doLast{ p ...

  3. gradle 指定springcloud 版本_springcloud小技能:服务注册发现如何隔离

    用过dubbo的都知道,dubbo服务发布&订阅有2个重要的参数:version和group.即消费者和生产者不但需要要接口名完全一致,还需要version和group也完全一致,才能成功的匹 ...

  4. gradle 指定java版本_Eclipse使用gradle编译时,使用固定的jdk版本进行编译(修改gradle的jdk编译版本)...

    很多时候我们使用多个版本的jdk,比如说,用的是1.7 ,开发环境用1.8 那么经常碰到编译后的包是1.8版本的,虽然设置了1.7的,因为很多时候我们使用打包工具了. 1.7和1.8有个问题就是1.8 ...

  5. ant指定servlet版本_阅读SpringMVC源码前,不妨看下简易版本SpringMVC框架的搭建

    开发环境:windows10.idea.jdk1.8.apache-tomcat-9.0.0.M3 SpringMVC框架是基于Servlet设计的,所以如果你知道SpringMVC,但是没听过道Se ...

  6. gradle 查看依赖类库版本_如何查找第三方库(Gradle引用)的依赖?

    原标题:如何查找第三方库(Gradle引用)的依赖? 如何查找第三方库(Gradle引用)的依赖? 每日一问-Tools-20181105 答: 三方法可查找. 1. 执行 Gradle Task : ...

  7. ant指定servlet版本_[转载]程序开发常见错误

    [转载]程序开发常见错误 (2011-06-19 14:28:13) 标签: 转载 谢谢 项目中遇到的错误 sql错误 找不到列 Unknown column 'fillMen' in 'field ...

  8. spark指定python版本_如何将正常的Python应用程序正确转换为PySpark版本

    我是PySpark的初学者,最近我尝试向我的Spark集群提交一个简单的python应用程序(批量调整大小图片).我可以成功地通过pycharm运行该应用程序,并且当我将应用程序提交给spark时,图 ...

  9. SpringCloud教程- 服务消费者(Feign)(SpringCloud版本Finchley)

    文章目录 一.Feign简介 二. 环境准备 三.创建基于Feign服务 定义启动类 pom文件配置 配置文件application.yml 定义一个feign接口 定义一个controller 前言 ...

最新文章

  1. Set和存储顺序深入探讨、SortedSet排序的示例
  2. iOS Automated Tests with UIAutomation
  3. 有关于mfc webbrowser插件的使用
  4. 软件保障与测试课程实践记录:贪吃蛇小程序
  5. 你的代码是否按照高内聚、低耦合的原则来设计的?
  6. mysql 备份的脚本
  7. 【xshell】xshell 自动换行设置
  8. dorado7-发布
  9. jquery知识点总结二
  10. MySQL多表关联查询与存储过程
  11. mysql 导出过长的数字列时变科学计数法问题解决办法
  12. SQL语句详解(一)——基本增删改操作
  13. eclipse mac oracle数据库,Eclipse连接Oracle数据库的具体步骤
  14. [HNOI2003]多边形
  15. 常用c语言代码大全,C语言的一些常用代码
  16. 问道虚拟机服务器地址,问道架设安装详细说明
  17. JAVA Graphics2D种drawLine方法
  18. 计算机系一班班会,天津科技大学计算机学院读书节10102i1班班会.ppt
  19. PostgreSQL数据库——Pigsty configure
  20. 利用二进制位求平均值

热门文章

  1. Android开发之修改Chrome书签
  2. python中的模块与类
  3. python中级_python复习之中级
  4. python路径规划仿真实验_【python实战】批量获得路径规划——高德地图API
  5. python 函数 过程_python之函数篇
  6. excel两个指标相关性分析_我用Excel发现了数据分析的本质:回归分析
  7. php 子类重新定义父类的变量_PHP设计模式 ——(抽象工厂模式)
  8. 编写一个watchdog.sh脚本_拍摄Vlog,如何构思和编写脚本?
  9. jdk1.8 Windows安装全过程详尽版
  10. oracle exception 循环,Oracle Exception In Loop