mflyyou.cn/dev-tools/g… Gradle

环境介绍

  • OpenJDK 17.0.5
  • Gradle 7.6
  • 示例代码 fly-gradle

Gradle 项目下文件介绍

假如你的电脑安装了 gradle,能够运用 gradle init 去初始化一个新的 gradle 工程,然后运用电脑安装的 gradle 去履行构建指令。

可是每个开发电脑上的 gradle 版别不一样,为了统一构建环境,咱们能够运用 gradle wrapper 限制项目依靠 gradle 的版别。

# 会生成 gradle/wrapper/* gradlew gradlew.bat
gradle wrapper --gradle-version 7.5.1 --distribution-type bin
.
├── build.gradle
├── gradle
│ └── wrapper
│     ├── gradle-wrapper.jar
│     └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle

gradlew 和 gradlew.bat

运转项目 wrapper 下界说的 gradle 去构建项目。

gradlew 是 macos 和 linux 体系下。

gradlew.bat 是 windows 体系下运用的。

wrapper

Gradle 浅入浅出

wrapper 界说项目依靠那个版别的 gradle,假如本地 distributionPath 没有对应版别的 gradle,会自动下载对应版别的 gradle。

# gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# 假如是国内项目,只需要修改这个url 就能够提高下载速度
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

GRADLE_USER_HOME 没有装备的话,默许是 ~/.gradle

zipStoreBasezipStorePath 界说了下载的 gradle (gradle-7.6-bin.zip) 存储的本地途径。

distributionBasedistributionPath 界说下载的 gradle 解压的本地目录。

gradlew 实践是运转 gradle-wrapper.jar 中的 main 办法,传递给 gradlew 的参数实践上也会传递给这个 main 办法。

gradle-wrapper.jar 会判别是否下载 wrapper 装备的 gradle,并且将传递参数给下载的 gradle,并运转下载的 gralde 进行构建项目。

升级 wrapper 界说的 gradle 版别

./gradlew wrapper --gradle-version 7.6

settings.gradle

pluginManagement {
    repositories {
        maven {
            url="file://${rootDir}/maven/plugin"
        }
        gradlePluginPortal()
    }
    plugins {
      	// spring_boot_version 能够在 gradle.properties 装备
        id 'org.springframework.boot' version "${spring_boot_version}"
    }
}
rootProject.name = 'fly-gradle'
include 'user-manage-service','user-manage-sdk'
include 'lib-a'
include 'lib-b'

settings.gradle 首要用于装备项目名称,和包含哪些子项目。

也能够用于装备插件的依靠版别(不会运用到项目中去,除非项目运用这个插件)和插件下载的

build.gradle

build.gradle 是对某个项目的装备。装备 jar 依靠关系,界说或者引进 task 去完结项目构建。

gradle.properties

首要用于装备构建进程中用到的变量值。也能够装备一些 gradle 内置变量的值,用于修改默许构建行为。

org.gradle.logging.level=quiet
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xms512m -Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

org.gradle.jvmargs 用来装备 Daemon 的 JVM 参数,默许值是 -Xmx512m "-XX:MaxMetaspaceSize=384m"

当咱们的项目比较大的时分,可能会因为 JVM 堆内存不足导致构建失利,就需要修改此装备。

org.gradle.logging.level 调整 gradle 的日志级别。参考 gradle logging 挑选想要的日志级别。

Gradle Daemon

为加快项目构建,gralde 会启动一个常驻 JVM 后台进程去处理构建,Daemon 进程默许三小时过期且当内存压力大的时分也会关闭。

Gradle 默许会启用 Daemon 进程去构建项目。

# 检查 daemon 运转状况
./gradlew --status
# stop daemon 进程
./gradlew --stop
# 重启 daemon 进程
./gradlew --daemon

构建生命周期

Gradle 是依据 task 依靠关系来构建项目的,咱们只需要界说 task 和 task 之间的依靠关系,Gradle 会确保 task 的履行次序。

Gradle 在履行 task 之前会树立 Task Graphs,咱们引进的插件和自己构建脚本会往这个 task graph 中增加 task。

Gradle 浅入浅出

Gradle 的构建进程分为三部分:初始化阶段、装备阶段和履行阶段。

初始化阶段

  • 找到 settings.gradle,履行其中代码
  • 确定有哪些项目需要构建,然后对每个项目创立 Project 目标,build.gradle 首要便是装备这个 Project 目标
// settings.gradle
rootProject.name = 'basic'
println '在初始化阶段履行'

装备阶段

  • 履行 build.gradle 中的装备代码,对 Project 进行装备
  • 履行 Task 中的装备段句子
  • 依据请求履行的 task,树立 task graph
println '在装备阶段履行 Task 中的装备段句子'
tasks.register('configured') {
  println '在装备阶段履行 Task 中的装备段句子'
  doFirst {
    println '在履行阶段履行'
  }
  doLast {
    println '在履行阶段履行'
  }
}

履行阶段

依据 task graph 履行 task 代码。

依靠办理

Maven 私服装备

咱们一般都是多项目构建,因此只需要在父项目 build.gradle 装备 repositories。

allprojects {
  repositories {
    maven {
      url "${mavenPublicUrl}"
      credentials {
        username "${mavenUsername}"
        password "${mavenPassword}"
      }
    }
    mavenLocal()
    mavenCentral()
  }
}

credentials 装备账号密码,当私服不需要权限下载的时分能够不装备。

Gradle 会依照装备的库房次序查询依靠下载。

装备依靠来自某个目录

dependencies {
	compile files('lib/hacked-vendor-module.jar')
}
dependencies {
	compile fileTree('lib')
}

有的时分第三方库没有 maven 供咱们运用,能够运用这个。

依靠抵触

默许依靠抵触

::: tip 当出现依靠抵触的时分,gradle 优先挑选版别较高的,因为较高版别会兼容低版别。 :::

dependencies {
    implementation 'com.google.guava:guava:31.1-jre'
    implementation 'com.google.code.findbugs:jsr305:3.0.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}

咱们能够履行下面指令检查项目依靠的版别:

./gradlew dependency-management:dependencies --configuration compileClasspath
------------------------------------------------------------
Project ':dependency-management'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:3.0.2
+--- com.google.guava:guava:31.1-jre
|    +--- com.google.guava:failureaccess:1.0.1
|    +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|    +--- com.google.code.findbugs:jsr305:3.0.2
|    +--- org.checkerframework:checker-qual:3.12.0
|    +--- com.google.errorprone:error_prone_annotations:2.11.0
|    \--- com.google.j2objc:j2objc-annotations:1.3
\--- com.google.code.findbugs:jsr305:3.0.0 -> 3.0.2

咱们能够看到,gradle 挑选了 com.google.code.findbugs:jsr305:3.0.2 这个版别。

强制运用某个版别

假如咱们想运用 com.google.code.findbugs:jsr305:3.0.0 版别

dependencies {
    implementation 'com.google.guava:guava:31.1-jre'
    implementation 'com.google.code.findbugs:jsr305:3.0.0', {
        force = true
    }
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
./gradlew -q dependency-management:dependencyInsight --dependency jsr305 --configuration compileClasspath
com.google.code.findbugs:jsr305:3.0.0 (forced)
  Variant compile:
    | Attribute Name                 | Provided | Requested    |
    |--------------------------------|----------|--------------|
    | org.gradle.status              | release  |              |
    | org.gradle.category            | library  | library      |
    | org.gradle.libraryelements     | jar      | classes      |
    | org.gradle.usage               | java-api | java-api     |
    | org.gradle.dependency.bundling |          | external     |
    | org.gradle.jvm.environment     |          | standard-jvm |
    | org.gradle.jvm.version         |          | 17           |
com.google.code.findbugs:jsr305:3.0.0
\--- compileClasspath
com.google.code.findbugs:jsr305:3.0.2 -> 3.0.0
\--- com.google.guava:guava:31.1-jre
     \--- compileClasspath

禁用依靠传递

guava 不会传递依靠它依靠的库到当时库,能够看到

dependencies {
    implementation 'com.google.guava:guava:31.1-jre', {
        transitive = false
    }
    implementation 'com.google.code.findbugs:jsr305:3.0.0'
}
./gradlew dependency-management:dependencies --configuration compileClasspath
------------------------------------------------------------
Project ':dependency-management'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:3.0.2
+--- com.google.guava:guava:31.1-jre
\--- com.google.code.findbugs:jsr305:3.0.0

能够看到 guava 依靠的 jar 没有传递到当时项目中来。

排除某个依靠

Guava 依靠的别的 jar 能够传递进来,并且排除了 findbugs, 项目依靠的版别为 3.0.0

dependencies {
	 implementation 'com.google.guava:guava:31.1-jre', {
        exclude group: 'com.google.code.findbugs', module: 'jsr305'
    }
    implementation 'com.google.code.findbugs:jsr305:3.0.0'
}
./gradlew dependency-management:dependencies --configuration compileClasspath
------------------------------------------------------------
Project ':dependency-management'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:3.0.2
+--- com.google.guava:guava:31.1-jre
|    +--- com.google.guava:failureaccess:1.0.1
|    +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|    +--- org.checkerframework:checker-qual:3.12.0
|    +--- com.google.errorprone:error_prone_annotations:2.11.0
|    \--- com.google.j2objc:j2objc-annotations:1.3
\--- com.google.code.findbugs:jsr305:3.0.0

能够看到 guava 传递到当时项目的依靠少了 findbugs

装备依靠之间继承

configurations {
    integrationTestImplementation.extendsFrom testImplementation
    integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
}
configurations.all {
    resolutionStrategy {
        force 'org.apache.tomcat.embed:tomcat-embed-core:9.0.43'
    }
    exclude group: 'org.slf4j', module: 'slf4j-simple'
}

api 和 implementation 差异

jar b 包含一下依靠

dependencies {
    api 'org.apache.commons:commons-lang3:3.12.0'
    implementation 'com.google.guava:guava:31.1-jre'
}

项目 a 引进 jar b ,commons-lang3 和 guava 都能够被工程 a 运用,只是二者 scope 不一样。

api 对应 compile,在 工程 a 能够直接运用,编译能够经过。

implementation 对应 runtime,编译找不到 guava 中的类。

Task

咱们引证的插件实践是增加 task 到 task graph 中去。

咱们知道 build.gradle 实践是用来装备对应项目的 org.gradle.api.Project。

因此咱们能够在 build.gradle 中引证 org.gradle.api.Project 露出的属性。

咱们能够在 gradle dsl 和 Project 接口中能够知道能够访问哪些属性。

tasks 实践便是 Project 露出的一个属性,因此咱们能够运用 tasks 往当时项目中注册 task。

tasks.register('hello') {
    doLast {
        println 'hello'
    }
}

推荐运用 tasks.register 去注册 task,而不是 tasks.create 去直接创立。

tasks.register 注册的 task 只会在用到的时分才会初始化。

Groovy 闭包

Groovy Closures

{ [closureParameters -> ] statements }

闭包的示例

{ item++ }
{ -> item++ }                                       
{ println it }                                      
{ it -> println it }                                

:::tip

当办法的最后一个参数是闭包时,能够将闭包放在办法调用之后。

:::

比如注册一个 task 的接口是

register(String var1, Action<? super Task> var2)
tasks.register("task55"){
    doFirst {
        println "task55"
    }
}
tasks.register("task66",{
    doFirst {
        println "task66"
    }
})

Task Type

gradle 已经界说好一些 task type,咱们能够运用这些 task type 帮助咱们完结特定的工作。比如咱们想要履行某个 shell 指令。

Exec – Gradle DSL Version 7.6

tasks.register("task3", Exec) {
    workingDir "$rootDir"
    commandLine "ls"
}

Plugin

插件分类

Gradle 有两种类型的插件 binary plugins and script plugins

二进制插件便是封装好的构建逻辑打成 jar 发布到线上,供别的项目运用。

脚本插件便是一个 *.gradle 文件。

buildSrc

一般咱们的项目是多项目构建,各个子项目会共享一些装备,比如 java 版别,repository 还有 jar publish 到哪里等等。

咱们能够将这些统一装备分组笼统为单独的插件,子项目引证这个插件即可。便于保护,不用在各个子项目都重复装备相同的东西。

buildSrc 这个目录必须在根目录下,它会被 gradle 自动识别为一个 composite build,并将其编译之后放到项目构建脚本的 classpath 下。

buildSrc 也能够写插件,咱们能够直接在子项目中运用插件 id 引进。

buildSrc/
├── build.gradle
├── settings.gradle
└── src
    ├── main
    │ ├── groovy
    │ │ └── mflyyou.hello2.gradle
    │ ├── java
    │ │ └── com
    │ │     └── mflyyou
    │ │         └── plugin
    │ │             ├── BinaryRepositoryExtension.java
    │ │             ├── BinaryRepositoryVersionPlugin.java
    │ │             └── LatestArtifactVersion.java

buildSrc/build.gradle

groovy-gradle-plugin 对应的是运用 groovy 写插件。

java-gradle-plugin 对应 java

plugins {
    id 'groovy-gradle-plugin'
    id 'java-gradle-plugin'
}
gradlePlugin {
    plugins {
        helloPlugin {
            id = 'com.mflyyou.hello'
            implementationClass = 'com.mflyyou.plugin.BinaryRepositoryVersionPlugin'
        }
    }
}

咱们就能够在子项目运用插件

plugins {
    id 'com.mflyyou.hello'
    id 'mflyyou.hello2'
}

插件运用

  • Applying plugins with the plugins DSL
plugins {
    id 'java'
}
  • Applying a plugin with the buildscript block
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
    }
}
apply plugin: 'com.jfrog.bintray'
  • Applying a script plugin
apply from: 'other.gradle'