Gradle开发手册-进阶篇

        在基础篇中了解了Gradle相关的知识同时通过一个例子实战了一下如何构建一个简单的Gradle-java应用。本章会详细讲解下gradle配置相关的详细内容。

  1. 基础篇:从概念以及广度上介绍下gradle的核心内容,并构建一个简单的java项目;
  2. 进阶篇:详细讲解Gradle的配置相关内容;
  3. 高级篇:讲述一些高级内容,比如多项目构建、自定义task等;
  4. 实战篇:构建几类常见的java应用,这些代码可以做为模板方便日后使用;
  5. FAQ篇:提供一些常用的命令和问题供查询;

        无论是使用本地安装,还是借助类似的Intellij idea工具构建Gradle应用,都会在本地系统中存在一个gradle的root目录,一般此目录存在于系统用户的根目录下,以笔者的macos系统为例,其根目录地址为:

/Users/liudong/.gradle 

        gradle的root目录存在的目的是为了方便资源共享和构建的管理,即gradle的root中的配置对所有本地的gradle应用都有效,同时每个本地的gradle应用都可以在build.gradle.kts进行配置覆盖root目录中的配置。在这个目录下两个文件夹需要关心一下:

Gradle开发手册-进阶篇_第1张图片

  •  cachs\modules-2:所有依赖的jar包的缓存目录;
  • wrapper\dists:所有下载的不同版本的Gradle存放地址目录;

一、Gradle全局配置

        gradle的root目录中存在的配置我们暂时称做全局配置。在此处可以配置2个文件init.gradle和gradle.properties

  • init.gradle:对应的gradle init命令,主要配置一些公共资源,比如仓库、公共jar包等;
  • gradle.properties:与运行相关,可配置一些运行时参数和自定义的参数,比如jvm参数等;  

        当然了,Gradle的配置比较多,笔者只挑了一些开发时比较重要的总结下,详细的可以查看官方文档。

        .gradle目录下最初时没有gradle.properties和init.gradle这两个文件,需要手动添加,这两个文件就是全局配置文件。但建议每到一个公司就修改一个目录(习惯问题,可按个人习惯决定是否分开)

1、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"
       }
   }
}

2、gradle.properties运行时配置

        在/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)

其它几个常用的配置如下:

  • org.gradle.unsafe.configuration-cache = true:

          开启gradle缓存,加快构建速度

  • org.jetbrains.intellij.buildFeature.selfUpdateCheck=false:

         设置插件是否自动更新检查

  • org.jetbrains.intellij.buildFeature.buildSearchableOptions=false:

         禁止为插件生成索引信息

  • org.jetbrains.intellij.buildFeature.paidPluginSearchableOptionsWarning=false:

        如果插件为付费的,设置是否可搜索;

  • kotlin.incremental.useClasspathSnapshot=false:

       Kotlin 编译器抛出内存不足时的解决方法

  • kotlin.stdlib.default.dependency = false:

       如果Kotlin版本大于1.4设置成false,否则设置成true

  • runIde.autoReloadPlugins = true:

       修改插件后可重新运行build插件,这样就会自动更新了

以上配置都可以在项目中的gradle.properties和buide.gradlw.kts构建脚本中覆盖。

二、Project中Gradle配置

        我们以上一篇文章中通过Intellij idea创建的的项目(单模块)为例进行配置扩展。上一章中创建的项目结构如下图所示,详细文件说明就累述了,可查看基础篇中的描述:

Gradle开发手册-进阶篇_第2张图片

         本节完整后的完整的项目结构会新增一个名为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开发手册-进阶篇_第3张图片

1、Gradle-Wrapper配置

        可以称Gradle Wrapper为Gradle包装器,是将Gradle再次包装。让所有的Gradle构建方法在 Gradle 包装器的帮助下运行。目的是可以让我们不需要在电脑中安装 Gradle 环境也可以运行 Gradle 项目。官方建议任何 Gradle 构建方法在 Gradle Wrapper帮助下运行,利于多人协同开发时环境的统一,在工程中会存在两个文件,其中gradle-wrapper.properties就是wrapper的配置文件。

  • gradle-wrapper.jar: 既然是 jar 包,那么它肯定是包含了 Gradle 运行时的逻辑代码;
  • gradle-wrapper.properties: 这个文件主要负责配置Gradle wrapper运行时的属性文件,声明具体使用哪个版本的 Gradle,详细配置如下:
#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。

2、gradle配置

        就是上面提到的用于提供运行时配置的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=我的用户

三、build构建脚本配置

        这个文件相当于maven的pom.xml配置文件,即gradle的所有行为全是在这个文件中定义的,可以采用Kotlin和groovy两种语言编写,本专题中笔者全部采用kotlin语言编写。主要的配置内容如下图所示:

Gradle开发手册-进阶篇_第4张图片

1、获取属性

        在gradle中大概有以下几种属性的配置方式,如果在多个地方配置了同名的参数,则优先级从高到底如下:

  1. 命令行属性:在输入命令时通过-D附带参数,比如 $ gradle -DgradlePropertiesProp=commandLineValue--build-cache ;
  2. Gradle配置:root目录中gradle.properties文件中的配置;
  3. Project配置:项目中gradle..properties文件中的配置;
  4. 系统环境变量:比如window的环境变量和linux系统下的.bash_profile中的配置;

        建议采用第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"))

2、project基本配置

       以下所有小节的内容代码是增量添加的关系。需要手动拷贝到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/"
        }
    }
}

3、project依赖配置

 插件配置

        gradle插件类似于maven插件,plugins标签里的插件必须是gradle官方插件库(​​http://plugins.gradle.org​​​)里的,另外plugins块不能放在多项目配置块(allProjects, subProjects)里。中添加一个id内容,就会在右侧插件UI上多出一个可点击的任务类型。

将插件应用于项目允许插件扩展项目的功能,它可以做如下事情:

  • 扩展 Gradle 模型(例如添加可配置的新 DSL 元素)
  • 根据约定配置项目(例如添加新任务或配置合理的默认值)
  • 应用特定配置(例如添加组织存储库或执行标准)

通过应用插件,而不是向项目构建脚本添加逻辑,我们可以获得很多好处,应用插件:

  • 促进重用并减少跨多个项目维护相似逻辑的开销
  • 允许更高程度的模块化,增强可理解性和组织性
  • 封装命令式逻辑并允许构建脚本尽可能具有声明性

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 中心仓库
}

jar依赖配置

          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:

  • implementation:默认的scope,会将指定的依赖添加到编译路径,并且会将该依赖打包到输出,但是这个依赖在编译时不能暴露给其他模块,例如如果我们的类库包含了gson,那么其他人使用我们的类库时,编译时不会出现gson的依赖。
  • api:api关键字是由java-library提供,若要使用,请在plugins中添加:​​id 'java-library'​​== (在旧版本中作用与compile相同,新版本移除了compile)使用api配置的依赖会将对应的依赖添加到编译路径,并将依赖打包输出,但是这个依赖是可以传递的,比如模块A依赖模块B,B依赖库C,模块B在编译时能够访问到库C,但是与implemetation不同的是,在模块A中库C也是可以访问的。
  • compileOnly:compileOnly修饰的依赖会添加到编译路径中,但是不会被打包,因此只能在编译时访问,且compileOnly修饰的依赖不会传递。
  • runtimeOnly:这个与compileOnly相反,它修饰的依赖不会添加到编译路径中,但是能被打包,在运行时使用。和Maven的provided比较接近。
  • annotationProcessor:用于注解处理器的依赖配置。
  • testImplementation:这种依赖在测试编译时和运行时可见,类似于Maven的test作用域。
  • testCompileOnly和testRuntimeOnly:这两种类似于compileOnly和runtimeOnly,但是作用于测试编译时和运行时。
  • classpath:见上一段,classpath并不能在buildscript外的dependcies中使用

jar包依赖排除配置

        开发过程中,或多或少都会遇到 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配置

        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代码块是提供给用户自定义属性用的,一般用来定义版本,实现版本的集中管理

四、java-plug插件任务详解

        引入插件

plugins {
    java
}

1、常用任务

Gradle开发手册-进阶篇_第5张图片

compileJava— JavaCompile

        Depends on:所有有助于编译类路径的任务,包括jar来自通过项目依赖项位于类路径上的项目的任务,使用 JDK 编译器编译生产 Java 源文件。

processResources—ProcessResources

        将生产资源复制到生产资源目录中。

classes

        取决于compileJavaprocessResources,这是一个仅依赖于其他任务的聚合任务。其他插件可能会附加额外的编译任务。

compileTestJava— JavaCompile

        取决于:classes以及对测试编​​译类路径有贡献的所有任务,使用 JDK 编译器编译测试 Java 源文件。

processTestResources—copy

        将测试资源复制到测试资源目录中。

testClasses

        取决于compileTestJavaprocessTestResources,这是一个仅依赖于其他任务的聚合任务。其他插件可能会附加额外的测试编译任务。

jar—jar

        取决于classes,根据附加到main源集的类和资源组装生产 JAR 文件。

javadoc— Javadoc

        取决于classes,使用 Javadoc 为生产 Java 源代码生成 API 文档。

test—test

        取决于:testClasses以及生成测试运行时类路径的所有任务,使用 JUnit 或 TestNG 运行单元测试。

clean—clean

        删除项目构建目录。

cleanTaskName—clean

        删除指定任务创建的文件。例如,cleanJar将删除任务创建的 JAR 文件jarcleanTest将删除任务创建的测试结果test

2、源码处理相关

compileSourceSetJava— JavaCompile

        取决于:对源集的编译类路径有贡献的所有任务使用 JDK 编译器编译给定源集的 Java 源文件。

processSourceSetResources—copy

        将给定源集的资源复制到资源目录中。

sourceSetClasses—task

        取决于:,compileSourceSetJavaprocessSourceSetResources准备给定源集的类和资源以进行打包和执行。一些插件可能会为源集添加额外的编译任务。

3、生命周期相关

assemble

        取决于jar,以及创建附加到archives配置的工件的所有其他任务聚合项目中所有档案的聚合任务。此任务由 Base Plugin 添加。

check

        取决于test,执行验证任务的聚合任务,例如运行测试。一些插件将自己的验证任务添加到checkTest如果您希望它们在完整构建中执行,您还应该将任何自定义任务附加到此生命周期任务。此任务由 Base Plugin 添加。

build

        取决于checkassemble。聚合执行项目完整构建的任务。此任务由 Base Plugin 添加。

buildNeeded

        Depends on :build和配置buildNeeded中依赖的所有项目中的任务testRuntimeClasspath执行项目及其依赖的所有项目的完整构建。

buildDependents

        Depends on :build和所有项目中的任务,这些项目在其配置buildDependents中具有该项目的依赖项testRuntimeClasspath执行项目和依赖它的所有项目的完整构建。

buildConfigName— taskRule

        取决于:生成附加到命名的工件的所有任务 -  ConfigName - 配置。为指定配置组装工件。此规则由 Base Plugin 添加。

你可能感兴趣的:(架构设计,java,开发语言,intellij-idea,gradle,spring)