本文简要介绍了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/*
如下图
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
(2)apply plugin,plugins
声明gradle脚本使用什么插件,apply plugin单个插件,plugins 多个插件。
一般来说,加一个 ‘java’ 就满足需求了
例1:
apply plugin: 'java' plugins { id 'application' id 'java' }
例2,如下图:
(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'里面的代码了。
(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接口
增强型task的官方例子
https://docs.gradle.org/current/userguide/java_plugin.html
增强型可选参数
例1,简单的task
例2,增加型task
例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
(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:
活动action
对泛型T执行某些操作。
(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
以下配置指定了,下载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是查看运行配置关系的
(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侧边栏的功能相同。
(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") { // 执行相应的编译逻辑 } }