Gradle使用教程原创
金蝶云社区-吴锐雄
吴锐雄
33人赞赏了该文章 2万次浏览 未经作者许可,禁止转载编辑于2023年12月06日 14:54:36
summary-icon摘要由AI智能服务提供

本文简要介绍了Gradle及其相关概念,包括Gradle的官方文档、插件搜索、Java、Spring、Android等示例,以及Gradle脚本中的自定义task、Task的API、常用TaskContainer函数等。详细讲解了Gradle构建工程的主要模块,如build.gradle、setting.gradle,并展示了apply plugin、dependencies、repositories、task定义、TaskContainer管理、buildscript、allprojects/subprojects、Closure和Action、sourceSets、def定义函数和变量等关键配置和用法。通过这些介绍,读者可以了解Gradle的基本操作和高级特性,以更好地使用Gradle进行项目构建和管理。

创作不易,如果文章对您有帮助,请为我点击一个朴实无华的赞^_^

一、官方文档和gradle介绍

Gradle官网:https://docs.gradle.org/

 

关键字文档:https://docs.gradle.org/current/dsl/

 

Gradle插件搜索:https://plugins.gradle.org/

 

Java,Spring,Android等例子:https://docs.gradle.org/current/samples/index.html

 

自定义task的文档:https://docs.gradle.org/current/userguide/custom_tasks.html

 

Task的api文档:https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html

 

常用的TaskContainer函数:

https://docs.gradle.org/current/dsl/org.gradle.api.tasks.TaskContainer.html

 

构建资料文档:

https://docs.gradle.org/current/userguide/building_java_projects.html

 

个人对gradle和Groovy的理解

Gradle使用Groovy语言。

我们可以像写java代码一样,用gradle去写一个构建项目的脚本,只是语法上gradle有些不一样,在gradle里面,也有对象,函数,参数,变量,接口,继承这些概念。

 

官网上的教程是从安装gradle开始,如果使用idea开发工具,可以直接创建gradle工程,不需要安装gradle。


在阅读本篇文章时,建议拿一个gradle工程对照着看,一边看文章一边看gradle工程,事半功倍。

二、gradle构建一个工程主要分为几个模块:

build.gradle、setting.gradle、gradle/*

如下图

image.png


build.gradle

(1)interface  Project

第(1)点可以跳过不看,我为了让各位开发者了解 build.gradle是一个什么东西才写了第一项。

Project接口不需要开发者编写,gradle已经默认实现了,从第(2)点开始,内容全是build.gradle的各个配置项


一个build.gradle全部功能都是这个接口提供的,我个人理解,我们可以把一个 build.gradle 理解为一个Project接口 的实例。build.gradle即是一个Project对象,Project就是当前这个gradle工程项目。


我们在一个build.gradle文件中,按住 ctrl+鼠标左键,点击dependencies,可以进入Project接口。

感兴趣的开发者,可以研究一下gradle的创建过程和Project接口代码。

也可以查阅官方文档。

https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html

https://docs.gradle.org/current/dsl/org.gradle.api.Project.html

image.png



(2)apply plugin,plugins

声明gradle脚本使用什么插件,apply plugin单个插件,plugins 多个插件。

一般来说,加一个 ‘java’ 就满足需求了

例1:

apply plugin: 'java'
plugins {
    id 'application'
    id 'java'
}

例2,如下图:

image.png

 

(3)dependencies

可以依赖本地jar,或者远程线上的jar(经常和repositories一起使用)

 

依赖仓库有很多种写法,这里仅举例几种写法

 

例1,依赖远程仓,依赖远程仓加版本号

dependencies {
    api 'org.hibernate:hibernate-core:3.6.7.Final'
}

或者可以在外部定义版本号

def springBootVersion = '2.0.3.RELEASE'
dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    classpath("org.springframework:springloaded:${springBootVersion}")
    classpath("org.springframework.build.gradle:propdeps-plugin:0.0.6")
}

 

 

例2,依赖本地仓,依赖单个jar和多个jar

repositories {
    flatDir {
        dirs 'libs'
    }
}
dependencies {
    api fileTree(include: ['*.jar'], dir: 'libs')
}
dependencies {
    Implementation files('libs/your-library-file-name.jar')
}


例3,类似于maven,也可以做一个排除

implementation("org.springframework.boot:spring-boot-starter:$project.ext.springBootVersion"){
    exclude module: 'org.springframework.boot:spring-boot-starter-logging'
}

 

例4,依赖本地module

build.gradle里面加入依赖

dependencies {
    compile project(':gdemo')
    compile project(':demo2')
}

 

setting.gradle配置module,'gdemo'是内部module, 'demo2'是外部module

include 'gdemo'
include 'demo2'
project(':demo2').projectDir = file('../demo2')


例4的本地依赖截图如下:

我的这个项目里面,有2个module,主module'node-debug-mservice',副module是'demo_plugin',

查看截图,可以看到在setting.gradle里面配置主module和其他副module。

查看截图,可以看到在'node-debug-mservice'的build.gradle里面,我配置了 当前的主module去 引用其他的module,这样配置了引用之后,'node-debug-mservice'就可以使用'demo_plugin'里面的代码了。

image.png


(4)dependencies使用时的一些函数介绍

implementation、api/compile、classpath

 

在gradle新版本中compile被替换为api,使用api或compile是等效的

 

api/compile 使用这个可以使依赖向上传递,

假设有3个模块,分别是baseMoudle、myModule、applaciton。

依赖关系是:myModule依赖了baseMoudle,applaciton 依赖了myModule,

在写dependencies 时用了api/compile,

这时,applaciton 也可以使用baseMoudle里面的代码。

 

使用implementation 则不会,将代码隐藏在内部,不对外部公开,不会对外传递依赖。

依照上述案例,依赖关系依旧是:myModule依赖了baseMoudle,applaciton 依赖了myModule。

编写依赖时,myModule使用implementation 去依赖baseMoudle,

applaciton里面使用不了baseMoudle里面的代码。

 

classpath 一般使用在buildscript里面

 

(5)repositories

声明gradle使用哪一种远程仓库集,这个需要和dependencies配合使用,当dependencies使用的是远程库时,会从远程仓库集中下载远程库到本地,再依赖使用。

如下例子,使用了三个远程仓库集,那么会从仓库集中找到一个远程库jar包下载到本地。

这些仓库集合其实大部分都是maven库,既支持maven又支持gradle。

jcenter()  https://jcenter.bintray.com/

mavenLocal()  直接使用本地maven仓库

mavenCentral()  https://repo1.maven.org/maven2/

如果使用远程仓库,可以直接上https://mvnrepository.com/ 查找全部jar包的版本号

 

可以直接在https://mvnrepository.com/repos

 

例1:

repositories {
jcenter()
    mavenLocal()
    mavenCentral()
}

例2,参照阿里云,我们也可以自己搭建一个仓库集合,使用url的方式加载:

repositories {
    maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
    maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
}

 

(6)task 任务

可以定义一个任务,使用命令行执行该任务gradle taskName,或者在idea的侧边gradle工具栏里面执行这个task。

官方文档:https://docs.gradle.org/current/userguide/custom_tasks.html

有两种task,一种是我们自己定义的简单task。一种是官方给我们写好的增强型task。

 

还可以在增强型task的基础上再自定制一个task。

 

增强型task的类型,请查看api

https://docs.gradle.org/current/javadoc/org/gradle/api/Task.html

 

如果是在build.gradle中使用的task,就是使用Interface Project的api,去创建一个task对象,这个对象继承了task接口

 

图片1.png

 

增强型task的官方例子

https://docs.gradle.org/current/userguide/java_plugin.html

 

增强型可选参数

 

图片2.png


例1,简单的task

图片3.png

 

 


例2,增加型task

图片4.png

 


例3,执行特殊的task脚本,增强型task

我们可以定义一个task,声明任务名,声明类型(type)。

例如声明一个task,函数名为copyJarTodir,类型为Copy,这个Copy类型是gradle的插件提供的一个已经实现好的功能,

org.gradle.api.tasks.Copy
task copyJarTodir(type: Copy) {
    from 'build/libs'
    into '/home/local/bos'
    exclude '**/*.class'
}

 

例4,参照官方教程,在不使用project的api的场景下,创建一个task类,并实例化它,变量名就是”hello”

class GreetingTask extends DefaultTask {
    @TaskAction
    def greet() {
        println 'hello from GreetingTask'
    }}
// Create a task using the task type
task hello(type: GreetingTask)

 

(7)TaskContainer

负责管理一组Task实例。有关TaskContainer的用法和函数查阅的api链接,以下举出几个常用的TaskContainer函数。

https://docs.gradle.org/current/dsl/org.gradle.api.tasks.TaskContainer.html

 

关于TaskContainer,我们可以自己去创建操作一个task,也可以使用TaskContainer来创建操作task。

 

查找task

findByPath(path: String):

withType(type: Class): TaskCollection

 

创建task

create(name: String):

 

当task被加入到TaskContainer时的监听

whenTaskAdded(action: Closure)

 

例1,云苍穹里面的task用法,查找Interface Project接口里面task: JavaCompile, 并把编码改为utf-8

tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}

 

例2,创建task

图片5.png

 

 

 

 

(8)buildscript

buildscript中的声明是gradle脚本自身需要使用的资源。可以声明的资源包括依赖项、第三方插件、maven仓库地址等。

而在build.gradle文件中直接声明的依赖项、仓库地址等信息是项目自身需要的资源。

如果构建脚本需要使用外部库,则可以将它们添加到构建脚本本身的脚本的类路径中。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
    }
}

 

(9)allprojects(),subprojects()

allprojects是对所有project的配置,包括Root Project。而subprojects是对所有Child Project的配置。

subprojects {
apply plugin: 'java'
    repositories {
        jcenter()
    }
}
allprojects {
    apply plugin: 'java'
    repositories {
        google()
    }
}

 

(10)Closure 和 Action

在gradle里面有很多和java类似的参数对象,例如 List,map,int,string,file

介绍一下我在gradle里面经常遇到的另外两种数据类型 Closure 和 Action

 

闭包closure它代表了一段可执行的代码,和java的 Lambda表达式有点像

 

Closure的定义格式,如果有参数,要加一个箭头 ->:

def xxx = {paramters -> code}

def xxx = {无参数,纯code}

例1:

图片6.png

活动action

对泛型T执行某些操作。

图片7.png

(11)sourceSets

指定Java源和资源文件的路径。

例1

sourceSets {
    main {
        java {
            srcDir 'src/java' // 指定源码目录
            srcDir "src/other/java"
            sourceSets.all{set ->
                println "${set.name}的文件是 ${set.java.srcDirs}"
            }

            exclude 'com/example/kylin/fristtest/MyTestOne.java'
        }
        resources {
            srcDir 'src/resources' //资源目录
        }
    }
}

 

 

(12)def

定义函数,使用

tasks.create(name: 'printTime') {
    String str = releaseTime();
    println str;
}
def releaseTime() {
    return new Date().format("yyyyMMdd hh:mm:ss", TimeZone.getTimeZone("GMT+8"))
}
定义变量,使用
def myStr = 123456;
tasks.create(name: 'printmy') {
    println "hello ${myStr}";
    println myStr;
}

(13)configurations

配置一组依赖,经常和dependencies 连用,具体可查看官方文档

https://docs.gradle.org/current/userguide/declaring_dependencies.html

 

例1,写一个smokeTest继承自testImplementation,并且再增加一个httpclient依赖。

然后就可以使用smokeTest去进行其他测试依赖。

configurations {
smokeTest.extendsFrom testImplementation
}
dependencies {
    testImplementation 'junit:junit:4.13'
smokeTest 'org.apache.httpcomponents:httpclient:4.5.5'
}

 

例2,排除,以下排除 'commons'和'shared'的代码应该是相同功能的

configurations {
    compile.exclude module: 'commons'
}
dependencies {
    compile("org.gradle.test.excludes:api:1.0") {
        exclude module: 'shared'
    }
}

 

(14)ConfigurationContainer

类似于TaskContainer,一个Configuration容器,可以用来创建Configuration或者获取Configuration。

例如:

configurations.create('myConfiguration')
configurations.myConfiguration {
    transitive = false
}

 

(15)创建对象

class A {
    def String name
    void m() {
        println('method m')
    }
}
def a = new A()
// 创建完成之后在task中执行
task printA{
    a.m()
    a.name = "gg"
    println(a.name)
}

 



setting.gradle

类似于project,也是一个接口org.gradle.api.initialization.setting

一般用来包含module,就是声明项目结构,导入代码模块的。导入了代码模块之后,就可以在build.gradle中依赖使用了。

 

例1,以下代码,确定了项目名称,包含了一个本地模块'gdemo',一个项目外的模块'demo2'

rootProject.name = 'node-debug-mservice'

include 'gdemo'

include 'demo2'
project(':demo2').projectDir = file('../demo2')


gradle/gradle-wrapper.properties

gradle-wrapperr.properties主要是做一些配置,配置gradle的版本和下载url,当gradle更新完成之后,gradle会下载大量的gradle工具包到本地。

官方文档:https://docs.gradle.org/current/dsl/org.gradle.api.tasks.wrapper.Wrapper.html

图片8.png

图片9.png

以下配置指定了,下载gradle-6.5-bin.zip包之后,把压缩包保存在 用户home目录下

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

 

也可以自己配置一个环境变量,GRADLE_USER_HOME,指定gradle的安装路径

 

其他选项不需要修改,distributionUrl可以修改url,更换gradle的版本。



三、构建project

构建资料文档

https://docs.gradle.org/current/userguide/building_java_projects.html

 

Gradle构建java项目,一般形式是打包编译jar,再运行jar。

或者定义task设置运行main方法

 

(1)定义task设置运行main方法

task runMain(dependsOn: 'classes', type: JavaExec) {
    main = 'kd.bos.debug.mservice.DebugServer'
    classpath = sourceSets.main.runtimeClasspath
}

 

(2)打包jar

参照云苍穹项目

task sourcesJar(type: Jar, dependsOn: build) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

也可以直接运行build,这个gradle原生定义好的task。

 

 

(3)侧边栏结构

侧边栏的结构,每一个module都有tasks,dependencies

主工程除了tasks,dependencies,还多一个run configurations

 

Tasks里面就是一些 gradle定义好的原生task,当然也可以自定义task,自定义完成之后,点击刷新,会出现在侧边栏 tasks/other下面。

最常用的就是tasks/build、tasks/other 下面的那些task。

 

dependencies和run configurations基本不会用到

dependencies是查看项目依赖关系的,可以查看到当前 主工程或者module依赖了哪些库

run configurations是查看运行配置关系的

图片10.png


图片11.png


图片12.png


(4)侧边栏常用task

一些常用task的意义可以查看官方文档

https://docs.gradle.org/current/userguide/java_plugin.html

 

compileJava 使用JDK编译器编译生产Java源文件,就是.class文件。

 

Jar 生成.class文件的同时,打jar包,compileJava的升级版

 

Assemble  多工程编译Multi-Projects Build,Assemble会连同本工程,再把所有的被依赖的module一起打包,jar的升级版

 

Build  是对整个工程进行彻底的重新编译,只针对更改过的文件进行编译,Assemble的升级版,build还能执行一些其他的脚步,理论上来说 Assemble和build没有区别

 

buildNeeded  Build的升级版

 

Clean 清理编译缓存,主要清理build/下的文件

 

(5)命令行

在idea里面,打开终端,或者使用cmd终端,可以通过命令行去执行task。

 

win系统中,在执行task时,其实就是在执行gradlew.bat,注意这里的gradlew命令和 “gradle” 相比多了一个w字母,敲命令时不要敲错。

 

例如 执行 gradlew clean  会清理所有的编译缓存,清理build目录下的文件

执行gradlew build  会重新生成build目录和一些class文件和jar包

执行命令行和idea侧边栏的功能相同。

图片13.png

 

(6)区分环境构建

可以自定义一些变量来区分不同的环境,在不同的环境下,可以使用不同的jar包、不同的变量,不同的编译策略。

// dev、test、pro
def env = "dev"
sourceSets {
    main {
        // gradle里面 可以用equals,也可以用==号
        if (env.equals("dev")) {
            srcDirs = "src/main/java/";
        } else if (env == "test") {
            srcDirs = "src/main/java/$env"
        } else if (env == "pro") {
            srcDirs = "src/main/java/$env"
        }
    }
}
def envJarsDir = "../lib" + env
dependencies {
    implementation fileTree(dir: envJarsDir, include: '*.jar')
}
task myAssemble(type: Assemble) {
    if (env.equals("dev")) {
        // 执行相应的编译逻辑
    } else if (env == "test") {
        // 执行相应的编译逻辑
    } else if (env == "pro") {
        // 执行相应的编译逻辑
    }
}








图标赞 33
33人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!

您的鼓励与嘉奖将成为创作者们前进的动力,如果觉得本文还不错,可以给予作者创作打赏哦!

请选择打赏金币数 *

10金币20金币30金币40金币50金币60金币
可用金币: 0