1.使用 Gradle 包装器
包装器是项目中包含的特殊脚本,用于下载正确的 Gradle 版本并执行构建。
使用包装器有 3 大优点:
如果你的项目中当前没有包装器,在 Linux 中可以通过运行 ./gradlew 来添加,在Windows 中使用 gradlew.bat ,那么就不会出错。
2.停止清理项目
如果你每次构建时都执行清理,那么会造成极大的时间浪费,如下:
./gradlew clean build
Gradle 有一个很的强功能,称为增量构建,意味着如果更改项目中的某些内容并运行构建,它只会根据该更改运行必要的任务。例如,如果只修改测试类,Gradle 不需要重新编译生产代码。增量构建意味着微小的更改构建速度会非常快,从而帮助开发人员完成更多工作。
3.添加settings.gradle
settings.gradle文件通常位于项目的根目录中,用于指定项目名称以及要添加到构建中的子项目。
示例:
rootProject.name = 'settings-example'
include 'some-subproject'
不过,settings.gradle是可选的。
如果缺省该文件,Gradle 将会使用基于目录名称的项目名称,如果项目被克隆到不同名称的目录,则其项目名称将不正确。例如,这可能发生在 CI 服务器上。
还有性能影响。如果省略settings.gradle,Gradle 会递归地在目录树中向上导航以查找此类文件。这可能会产生大量不必要的文件读取。
4. 将任务移至buildSrc
buildSrc目录位于项目的根目录中,可以包含 Groovy、Kotlin 或 Java 源代码。如果build.gradle中有一些任务代码,则可以转移到buildSrc,原因有 3 个:
- 清理build.gradle,使其更容易理解
- 将任务实现与声明分开
- 对于多项目构建,任务可以在其他子项目中使用
例如下面这个不太友好的 build.gradle 示例:
abstract class RollercoasterTask extends DefaultTask {
@Input
abstract Property getFavouriteCoaster()
RollercoasterTask() {
favouriteCoaster.convention('Space mountain')
}
@TaskAction
def tellMeMyFavourite() {
println "Your favourite coaster is ${favouriteCoaster.get()}!"
}
}
tasks.register('coaster', RollercoasterTask) {
favouriteCoaster = 'Super-duper loopy coaster'
}
随着build.gradle 的增长,这种代码会让你头晕目眩。解决办法就是在与build.gradle相同的级别创建buildSrc目录,结构如下:
├── build.gradle
├── buildSrc
│ └── src
│ └── main
│ └── groovy
│ └── com
│ └── tomgregory
│ └── RollercoasterTask.groovy
可以将类定义从build.gradle转移到RollercoasterTask.groovy中。只需包含相关的package和import 即可。如下:
package com.tomgregory
import org.gradle.api.DefaultTask
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
abstract class RollercoasterTask extends DefaultTask {
@Input
abstract Property getFavouriteCoaster()
RollercoasterTask() {
favouriteCoaster.convention('Space mountain')
}
@TaskAction
def tellMeMyFavourite() {
println "Your favourite coaster is ${favouriteCoaster.get()}!"
}
}
在build.gradle中,可以用单个 import 语句替换类定义。
import com.tomgregory.RollercoasterTask
tasks.register('coaster', RollercoasterTask) {
favouriteCoaster = 'Super-duper loopy coaster'
}
这样就清晰多了。
5. 并行运行测试
可以充分利用可用的 CPU 内核来并行运行测试,build.gradle 添加如下配置:
test {
maxParallelForks 3
}
完成此操作后,Gradle 将会并行执行测试。这里配置了3个执行器,可根据实际配置此参数。
6. 项目进行版本化
对 Gradle 项目进行版本控制可以让你更轻松地了解何时引入了更改。当其他人使用你的项目时,这一点尤其重要。
例如,Gradle 本身使用相当标准的版本号系统,其中包括主版本、次版本和补丁版本。
这样做的好处是 Gradle 用户,在升级 Gradle 时可以轻松了解更改的范围。当主要版本增加时,这表明可能会有重大更改,我们应该阅读发行说明。
在 Gradle 中,设置版本号是在build.gradle中完成的,如下所示:
version = '0.1.0'
7. 将任务声明封装在插件中
任务声明是创建某个任务类的实例时,通常配置一些任务属性。
如下创建了一个名为copyQuote的 Copy 任务:
project.tasks.register('copyQuote', Copy) {
from 'quote.txt'
into "$project.buildDir/quotes"
filter(ReplaceTokens, tokens: [CHARACTER: 'Tweedledee'])
}
事实上,在build.gradle中声明此任务可能会导致几个问题:
- 额外的代码使得从高层次理解build.gradle变得更加困难
- 如果在不同的子项目中声明类似的任务,可能会出现重复
解决方案是将逻辑移至插件中,如果只需要在自己的项目(而不是其他项目)中使用该插件,则可以在buildSrc目录中定义它。
.
├── build.gradle
├── buildSrc
│ └── src
│ └── main
│ ├── groovy
│ │ └── com
│ │ └── tomgregory
│ │ ├── WonderlandPlugin.groovy
将copyQuote任务移动到插件类中,代码如下:
package com.tomgregory
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Copy
class WonderlandPlugin implements Plugin {
void apply(Project project) {
def extension = project.extensions.create('wonderland', WonderlandPluginExtension)
project.tasks.register('copyQuote', Copy) {
from 'quote.txt'
into "$project.buildDir/quotes"
filter(ReplaceTokens, tokens: [CHARACTER: extension.characterName.get()])
}
}
}
现在build.gradle可以大大简化。
plugins {
id 'wonderland'
}
wonderland {
characterName = 'Tweedledee'
}
8. 优化存储库
在build.gradle中声明存储库告诉 Gradle 应该在哪里查找构建应用程序所需的依赖项。
例如,在这里我们告诉 Gradle 查看我的自定义本地 Maven 存储库和 Maven Central。
repositories {
maven {
name = 'tomRepo'
url 'http://localhost:8081/repository/snapshots'
allowInsecureProtocol true
credentials(PasswordCredentials)
}
mavenCentral()
}
假设同一个项目需要这些依赖项来构建 Java 应用程序。
dependencies {
implementation group: 'com.tom', name: 'artifact-to-publish', version: '1.0-SNAPSHOT'
implementation 'commons-lang:commons-lang:2.6'
implementation 'com.google.guava:guava:30.1.1-jre'
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
implementation 'org.hibernate:hibernate-validator:7.0.1.Final'
}
第一个依赖项来自本地 Maven 存储库,但其他依赖项可在 Maven Central 中找到。
9. 切勿提交密码
你是否曾经将密码提交到版本控制中然后遭受领导的鄙夷,或者被安全公司审查出安全问题。接下来我们将介绍如何避免。
不过,这已经成为过去,因为 Gradle 提供了许多我们可以将凭证移出项目的方法。
示例:
repositories {
maven {
name = 'tomsRepo'
url 'https://xxx.com/maven/demo/'
credentials(PasswordCredentials)
}
}
那么 Gradle 将自动查找属性Username和Password.
你可以在命令行上传递这些参数或在~/.gradle/gradle.properties中设置,如下:
// gradle.properties 中配置
tomsRepoUsername=myusername
tomsRepoPassword=mypassword
另一种方法是直接访问 Gradle 属性:
repositories {
maven {
url 'https://xxx.com/maven/demo/'
credentials {
username 'toms'
password property('mypw')
}
}
}
在命令行或~/.gradle/gradle.properties中传递密码。
命令行示例:
./gradlew build --refresh-dependencies -Pmypw=
在~/.gradle/gradle.properties示例:
mypw=mypassword