在基础篇中了解了Gradle相关的知识同时通过一个例子实战了一下如何构建一个简单的Gradle-java应用。本章会详细讲解下gradle配置相关的详细内容。
无论是使用本地安装,还是借助类似的Intellij idea工具构建Gradle应用,都会在本地系统中存在一个gradle的root目录,一般此目录存在于系统用户的根目录下,以笔者的macos系统为例,其根目录地址为:
/Users/liudong/.gradle
gradle的root目录存在的目的是为了方便资源共享和构建的管理,即gradle的root中的配置对所有本地的gradle应用都有效,同时每个本地的gradle应用都可以在build.gradle.kts进行配置覆盖root目录中的配置。在这个目录下两个文件夹需要关心一下:
gradle的root目录中存在的配置我们暂时称做全局配置。在此处可以配置2个文件init.gradle和gradle.properties。
当然了,Gradle的配置比较多,笔者只挑了一些开发时比较重要的总结下,详细的可以查看官方文档。
.gradle目录下最初时没有gradle.properties和init.gradle这两个文件,需要手动添加,这两个文件就是全局配置文件。但建议每到一个公司就修改一个目录(习惯问题,可按个人习惯决定是否分开)
主要是配置jar文件下载时的镜像地址,需要在/Users/liudong/.gradle文件夹中新建一个init.gradle初始化脚本,这样一来,gradle下载镜像的时候就会使用这里配置的镜像源下载,速度会快很多,脚本文件内容如下:
allprojects {
repositories {
maven {
url "https://maven.aliyun.com/repository/public"
}
maven {
url "https://maven.aliyun.com/repository/jcenter"
}
maven {
url "https://maven.aliyun.com/repository/spring"
}
maven {
url "https://maven.aliyun.com/repository/spring-plugin"
}
maven {
url "https://maven.aliyun.com/repository/gradle-plugin"
}
maven {
url "https://maven.aliyun.com/repository/google"
}
maven {
url "https://maven.aliyun.com/repository/grails-core"
}
maven {
url "https://maven.aliyun.com/repository/apache-snapshots"
}
}
}
在/Users/liudong/.gradle文件夹中直接新建一个gradle.properties文件,创建内容不复杂,直接看代码即可,设置Gradle的JVM参数、代理和日志等信息,更多的设置可查看官网。
#gradle运行时的jvm设置
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#gradle代理配置,按需配置即可
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=10800
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=10800
systemProp.file.encoding=UTF-8
org.gradle.warning.mode=all
#gradle运行时的性能配置
kotlin.incremental.useClasspathSnapshot = false
kotlin.stdlib.default.dependency = false
org.gradle.configuration-cache = true
org.gradle.caching = true
systemProp.org.gradle.unsafe.kotlin.assignment = true
#日志输出级别,默认为Info,如果只输出错误日志则指定quiet,命令行执行时需要指定参数-q
#./gradlew -q api:properties
org.gradle.logging.level=(quiet,warn,lifecycle,info,debug)
其它几个常用的配置如下:
开启gradle缓存,加快构建速度
设置插件是否自动更新检查
禁止为插件生成索引信息
如果插件为付费的,设置是否可搜索;
Kotlin 编译器抛出内存不足时的解决方法
如果Kotlin版本大于1.4设置成false,否则设置成true
修改插件后可重新运行build插件,这样就会自动更新了
以上配置都可以在项目中的gradle.properties和buide.gradlw.kts构建脚本中覆盖。
我们以上一篇文章中通过Intellij idea创建的的项目(单模块)为例进行配置扩展。上一章中创建的项目结构如下图所示,详细文件说明就累述了,可查看基础篇中的描述:
本节完整后的完整的项目结构会新增一个名为gradle.properties的项目配置文件。这个文件中的配置会自动被build.gradle.kts构建脚本引用,并通过编程的方式可以取得配置信息。一般来讲会配置与性能相关的参数,如缓存、jvm参数等,简单示例如下:
#性能配置
kotlin.incremental.useClasspathSnapshot = false
kotlin.stdlib.default.dependency = false
org.gradle.configuration-cache = true
org.gradle.caching = true
systemProp.org.gradle.unsafe.kotlin.assignment = true
完善后的项目结构如下,只新增了gradle.properties文件,详细的属性取值方法会在后面讲解,此处可以先忽略内容或文件内容置空即可:
可以称Gradle Wrapper为Gradle包装器,是将Gradle再次包装。让所有的Gradle构建方法在 Gradle 包装器的帮助下运行。目的是可以让我们不需要在电脑中安装 Gradle 环境也可以运行 Gradle 项目。官方建议任何 Gradle 构建方法在 Gradle Wrapper帮助下运行,利于多人协同开发时环境的统一,在工程中会存在两个文件,其中gradle-wrapper.properties就是wrapper的配置文件。
#Gradle根目录,默认根目录是/Users/userName/.gradle/;
distributionBase=GRADLE_USER_HOME
#distributionBase+distributionPath就是Gradle解包后的存放的具体目录
distributionPath=wrapper/dists
#Gradle项目中用到的Gradle版本,如果你使用IDEA的话推荐下载all版,这样可以分析源代码进而提供更加精确的gradle脚本支持
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
#Gradle压缩包下载后存储父目录
zipStoreBase=GRADLE_USER_HOME
#zipStoreBase+zipStorePath就是 Gradle 压缩包的存放位置
zipStorePath=wrapper/dists
此文件中唯一需要更改的就是distributionUrl参数,这是用来指定gradle的版本,比如采用8.0版本(建议采用8.1.1版本)默认值是gradle-8.0-bin.zip,但建议把bin(只有编译后的文件)改成all(包含源码),这样有利于脚本debug。
就是上面提到的用于提供运行时配置的gradle.properties文件,这个文件中可以配置两种类型的属性:1、运行时配置,因配置比较多,详细需要查看官网;2、自定义属性。此处只演示功能,暂时我们把内容配置如下:
#运行时参数
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.configuration-cache = true
org.gradle.caching = true
kotlin.incremental.useClasspathSnapshot = false
kotlin.stdlib.default.dependency = false
systemProp.org.gradle.unsafe.kotlin.assignment = true
#自定义用户属性
pluginSinceBuild = 221
org.gradle.project.foo=bar
#自定义系统属性
systemProp.gradle.wrapperUser=我的用户
这个文件相当于maven的pom.xml配置文件,即gradle的所有行为全是在这个文件中定义的,可以采用Kotlin和groovy两种语言编写,本专题中笔者全部采用kotlin语言编写。主要的配置内容如下图所示:
在gradle中大概有以下几种属性的配置方式,如果在多个地方配置了同名的参数,则优先级从高到底如下:
建议采用第3种,因为在多人协作开发时这样有利于环境的统一,因为其它三种全依赖本地环境。
另外每在plugins中添加一个id内容,就会在右侧插件UI上多出一个可点击的任务类型。
属性也有不同的分类,不同种类的属性相对的也会有4种不同的属性getter接口,大体如下。
自定义属性和gradle相关的属性
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
pluginSinceBuild = 221
org.gradle.project.foo=bar
提取API可以使用property和providers两种区别不大
println(providers.gradleProperty("org.gradle.jvmargs").get())
println(property("org.gradle.jvmargs"))
println(providers.gradleProperty("pluginSinceBuild").get())
println(property("org.gradle.project.foo"))
这种方式的属性也是自定义属性,但定义和取值的方式不同,这种是必须以systemProp开头的参数。
systemProp.gradle.wrapperUser=我的用户
提取API,注意key会省略systemProp
println(System.getProperty("gradle.wrapperUser"))
这种就是读取本地系统的环境变量内容,不需要定义。
println(System.getenv("ENVIRONMENTAL"))
以下所有小节的内容代码是增量添加的关系。需要手动拷贝到build.gradle.kts文件相应的位置上。
标识项目的类型,类型有很多,后面在实战章节会详细讲述,此处仅以发布为带有main.class的jar程序为例,其配置如下:
plugins {
id("application")
}
application {
mainClass.set("org.korgs.Main")
}
java
plugins {
id("java")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks {
withType {
sourceCompatibility = "17"
targetCompatibility = "17"
}
}
kotlin
plugins {
id("java")
id("org.jetbrains.kotlin.jvm") version "1.8.21"
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks {
compileKotlin {
kotlinOptions.jvmTarget = "17"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "17"
}
}
//本模块
sourceSets {
main {
java {
setSrcDirs(listOf("src"))
}
resources {
setSrcDirs(listOf("src/resources"))
}
}
test {
java {
setSrcDirs(listOf("test"))
}
resources {
setSrcDirs(listOf("test/resources"))
}
}
}
//外部引用,不是太常用
sourceSets {
main {
java {
srcDir("thirdParty/src/main/java")
}
}
}
group = 'com.zd'
version = '1.0-SNAPSHOT'
jar
plugins {
application
}
application {
mainClass.set("org.korgs.Main")
}
war
plugins {
war
}
tasks.war {
webAppDirectory.set(file("src/main/webapp"))
from("src/rootContent") // adds a file-set to the root of the archive
webInf { from("src/additionalWebInf") } // adds a file-set to the WEB-INF dir.
classpath(fileTree("additionalLibs")) // adds a file-set to the WEB-INF/lib dir.
classpath(moreLibs) // adds a configuration to the WEB-INF/lib dir.
webXml = file("src/someWeb.xml") // copies a file to WEB-INF/web.xml
}
打一个胖包
有两种方式,哪种都可以
// 将依赖打进jar包中,生成的文件名gradleDemo-1.0-SNAPSHOT.jar
tasks {
jar.configure {
duplicatesStrategy = org.gradle.api.file.DuplicatesStrategy.INCLUDE
from(configurations.runtimeClasspath.get().filter { it.name.endsWith("jar")}.map { zipTree(it) })
}
}
// 将依赖打进jar包中,生成的文件名gradleDemo-1.0-SNAPSHOT-uber.jar
tasks.register("uberJar") {
archiveClassifier.set("uber")
from(sourceSets.main.get().output)
dependsOn(configurations.runtimeClasspath)
from({
configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
})
}
plugins {
id 'maven-publish'
}
publishing {
publications {
myLibrary(MavenPublication) {
from components.java
}
}
repositories {
maven {
// default credentials for a nexus repository manager
credentials {
username 'admin'
password 'xxxxx'
}
allowInsecureProtocol = true //允许http
// 发布maven存储库的url
url "http://localhost:8080/nexus/content/repositories/releases/"
}
}
}
gradle插件类似于maven插件,plugins标签里的插件必须是gradle官方插件库(http://plugins.gradle.org)里的,另外plugins块不能放在多项目配置块(allProjects, subProjects)里。
将插件应用于项目允许插件扩展项目的功能,它可以做如下事情:
通过应用插件,而不是向项目构建脚本添加逻辑,我们可以获得很多好处,应用插件:
plugins {
id 'org.springframework.boot' version '2.7.4'
id 'java'
}
ps:因为java是核心插件,所以不用指定版本,而其它的插件比如org.springframework.boot是社区插件,必须指定版本。
gradle仓库可以直接使用maven的仓库,但是gradle下载的jar包文件格式与maven不一样,所以不能和maven本地仓库共用,仓库的配置如下:
repositories {
mavenLocal() //本地仓库
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } //外部仓库(阿里云)
mavenCentral() // maven 中心仓库
}
gradle依赖写在
scope(gropId:artifactId:version)格式。
dependencies {
testImplementation('junit:junit:4.13')
implementation('org.projectlombok:lombok:1.18.26')
implementation('mysql:mysql-connector-java:8.0.32')
}
Scope说明:maven只有compile、provided、test、runtime,而gradle有以下几种scope:
id 'java-library'
== (在旧版本中作用与compile相同,新版本移除了compile)使用api配置的依赖会将对应的依赖添加到编译路径,并将依赖打包输出,但是这个依赖是可以传递的,比如模块A依赖模块B,B依赖库C,模块B在编译时能够访问到库C,但是与implemetation不同的是,在模块A中库C也是可以访问的。开发过程中,或多或少都会遇到 jar 包冲突的问题,有时候两个 jar 包不同,但是里面有两个类的包名路径是一摸一样的。这样我们就需要排除掉某个包中的重复的类,这时候就需要用的 exclude 命令,如下例子:
排除关联依赖
compileOnly('org.hibernate:hibernate:3.1') {
exclude module: 'cglib' //通过artifact name来排除出
exclude group: 'org.jmock' //通过group name来排除
}
排除环境依赖
configurations {
runtime.exclude group: "org.slf4j", module: "slf4j-log4j12"
compile.exclude group: "org.slf4j", module: "slf4j-log4j12"
}
全局排除依赖
configurations.all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
buildscript中的声明是gradle脚本自身需要使用的资源。可以声明的资源包括依赖项、第三方插件、maven仓库地址等。而在build.gradle.kts文件中直接声明的依赖项、仓库地址等信息是项目自身需要的资源。gradle在执行脚本时,会优先执行buildscript代码块中的内容,然后才会执行剩余的build脚本。
buildscript代码块中的repositories和dependencies的使用方式与直接在build.gradle文件中的使用方式几乎完全一样。唯一不同之处是在buildscript代码块中你可以对dependencies使用classpath声明。该classpath声明说明了在执行其余的build脚本时,class loader可以使用这些你提供的依赖项。这也正是我们使用buildscript代码块的目的。某种意义上来说,classpath 声明的依赖,不会编译到最终的jar包里面。
import org.apache.commons.codec.binary.Base64
buildscript {
ext {
springBootVersion = "2.3.3.RELEASE"
}
repositories {
mavenCentral()
}
dependencies {
"classpath"(group = "commons-codec", name = "commons-codec", version = "1.2")
}
}
tasks.register("encode") {
doLast {
val encodedString = Base64().encode("hello world\n".toByteArray())
println(String(encodedString))
}
}
注意,上面代码中的ext代码块,ext代码块是提供给用户自定义属性用的,一般用来定义版本,实现版本的集中管理。
引入插件
plugins {
java
}
compileJava
— JavaCompile Depends on:所有有助于编译类路径的任务,包括jar
来自通过项目依赖项位于类路径上的项目的任务,使用 JDK 编译器编译生产 Java 源文件。
processResources
—ProcessResources将生产资源复制到生产资源目录中。
classes
取决于:compileJava
,processResources,
这是一个仅依赖于其他任务的聚合任务。其他插件可能会附加额外的编译任务。
compileTestJava
— JavaCompile 取决于:classes
以及对测试编译类路径有贡献的所有任务,使用 JDK 编译器编译测试 Java 源文件。
processTestResources
—copy将测试资源复制到测试资源目录中。
testClasses
取决于:compileTestJava
,processTestResources,
这是一个仅依赖于其他任务的聚合任务。其他插件可能会附加额外的测试编译任务。
jar
—jar 取决于:classes,
根据附加到main
源集的类和资源组装生产 JAR 文件。
javadoc
— Javadoc 取决于:classes,
使用 Javadoc 为生产 Java 源代码生成 API 文档。
test
—test 取决于:testClasses
以及生成测试运行时类路径的所有任务,使用 JUnit 或 TestNG 运行单元测试。
clean
—clean删除项目构建目录。
cleanTaskName
—clean 删除指定任务创建的文件。例如,cleanJar
将删除任务创建的 JAR 文件jar
,cleanTest
将删除任务创建的测试结果test
compileSourceSetJava
— JavaCompile取决于:对源集的编译类路径有贡献的所有任务使用 JDK 编译器编译给定源集的 Java 源文件。
processSourceSetResources
—copy将给定源集的资源复制到资源目录中。
sourceSetClasses
—task 取决于:,compileSourceSetJava
processSourceSetResources
准备给定源集的类和资源以进行打包和执行。一些插件可能会为源集添加额外的编译任务。
assemble
取决于: jar
,以及创建附加到archives
配置的工件的所有其他任务聚合项目中所有档案的聚合任务。此任务由 Base Plugin 添加。
check
取决于:test,
执行验证任务的聚合任务,例如运行测试。一些插件将自己的验证任务添加到check
. Test
如果您希望它们在完整构建中执行,您还应该将任何自定义任务附加到此生命周期任务。此任务由 Base Plugin 添加。
build
取决于:check
,assemble。
聚合执行项目完整构建的任务。此任务由 Base Plugin 添加。
buildNeeded
Depends on :build
和配置buildNeeded
中依赖的所有项目中的任务testRuntimeClasspath
。执行项目及其依赖的所有项目的完整构建。
buildDependents
Depends on :build
和所有项目中的任务,这些项目在其配置buildDependents
中具有该项目的依赖项testRuntimeClasspath
执行项目和依赖它的所有项目的完整构建。
buildConfigName
— taskRule取决于:生成附加到命名的工件的所有任务 - ConfigName - 配置。为指定配置组装工件。此规则由 Base Plugin 添加。