Gradle
概述
maven侧重jar包管理,gradle侧重项目的构建,构建性能远高于maven,特别是大型项目
一些开源框架已经由maven转向给了gradle,如spring
常见构建工具:
- Ant:2000年Apache推出的纯java编写的构建工具。
- 优点:使用灵活,速度快(快与maven和gradle)。
- 缺点是没有强加任何编码约定的项目目录,需要编写繁杂的xml文件构建指令
- Maven:2004年apache推出的使用xml和pom管理项目的构建工具
- 优点:遵循一套约定大于配置的项目目录结构,统一使用gav坐标管理依赖,侧重于包管理
- 缺点:项目构建过程过于僵化,配置文件编写不够灵活、不方便自定义组件,构建速度慢于gradle
- Gradle:2012年Google推出的基于Groovy语言的全新项目构建工具,集合了Ant和Maven各自的优势
- 优点:集Ant脚本的灵活性+Maven约定大于配置的项目目录优势,支持多种远程仓库,侧重于大型项目构建
- 缺点:学习成本高、资料少、脚本灵活、版本兼容性差
安装
- 下载:https://gradle.org/releases/ ,下载complete包,包含文档和源码
- 解压到指定目录,配置
GRADLE_HOME
,并配置bin目录到path gradle -v
检查是否安装成功,需要jdk环境- 配置
GRADLE_USER_HOME
为Gradle仓库地址,不要与maven共用一个仓库 - 也可以不安装,使用项目中的wrapper自动下载,wrapper里的gradle镜像可以使用国内镜像:
https\://mirrors.cloud.tencent.com/gradle/gradle-8.5-all.zip
目录
- build,编译后的字节码、打成的包
- gradle,存放wrapper的地方
- src,源码,一般会有一个子项目,src在子项目里
- main
- java
- resources
- test
- java
- resources
- main
- gradlew,调用wrapper脚本
- gradlew.bat,调用wrapper脚本
- buid.gradle,构建脚本,每个项目一个,不推荐在root项目里也有这个文件
- settings.gradle,设置文件,定义项目及子项目名称,如果没有子项目,也可以没有
Wrapper包装器
实际上是对gradle的一层包装,用于解决实际开发过程中可能遇到的不同项目需要不同版本的gradle问题。如果电脑上没有安装gradle或者版本太旧,这时候可以考虑使用gradle wrapper。这也是官方建议的原因,实际上有了gradle wrapper之后,本地是可以不配置gradle的。借助gradlew、gradlew.bat
脚本,它调用的就是工程中wrapper中的jar包,检查配置文件中的gradle是否已经安装,没有则去下载
gradle.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
#distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.5-all.zip
distributionUrl=file:///C:/xxx/xx/gradle-8.5-all.zip # 也可以写已经下载好的zip包的本地地址
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
升级gradlew中的版本:gradle wrapper --gradle-version=8.3
,只是修改properties中的版本号,自己直接改应该也可以吧
gradle wrapper执行流程:
- 第一次执行时会进行下载,下载到properties配置的目录下,
zipStoreBase=GRADLE_USER_HOME
,zipStorePath=wrapper/dists
- 如果没有配置环境变量
GRADLE_USER_HOME
,会使用当前用户下.gradle
目录
- 如果没有配置环境变量
- 并进行解压,解压到
distributionBase=GRADLE_USER_HOME
,distributionPath=wrapper/dists
目录下 - 并在
GRADULE_USER_HOME
环境变量下的cache
目录里进行缓存 distributionUrl
,发行版压缩包下载地址
新建一个项目时,使用本地的gradle即可,下载第三方的项目或旧项目使用gradlew
修改maven下载源
在gradle安装目录init.d目录下创建init.gradle(每次构建都会执行),ali镜像仓库说明:https://developer.aliyun.com/mvn/guide
mavenLocal()
需要配置maven安装目录的环境变量M2_HOME
- gradle下载的依赖会放在
GRADULE_USER_HOME
环境变量下的caches
目录里
allprojects {
repositories {
mavenLocal()
maven{name "Alibaba"; url "https://maven.aliyun.com/repository/public"}
maven{name "Bstek"; url "https://nexus.bsdn.org/content/groups/public"}
mavenCentral()
}
buildscript {
repositories {
maven{name "Alibaba"; url "https://maven.aliyun.com/repository/public"}
maven{name "Bstek"; url "https://nexus.bsdn.org/content/groups/public"}
maven{name "M2"; url "https://plugins.gradle.org/m2"}
}
}
}
Gradle常用命令
gradle的指令要在含有build.gradle的目录执行
gradle clean
,清空build目录gradle classes
,编译业务代码和配置文件gradle test
,编译测试代码,生成测试报告gradle build
,构建项目,会执行前面的classes、testgradle build -x test
,跳过测试构建项目
Groovy简介
官网:http://www.groovy-lang.org/
在某种程度上,Groovy可以被视为Java的一种脚本化改良版,Groovy也是运行在JVM上,可以很好的与Java代码及相关库进行交互操作,它是一种成熟的面向对象编程语言,既可以面向对象编程,又可以用作纯粹的脚本语言。大多数有效的Java代码也可以转换为有效的Groovy代码,和Java的主要区别是,完成同样的任务所需要的的Groovy代码比Java代码更少。
特点:
- 功能强大,例如提供了动态类型转换,闭包和元编程支持
- 支持函数式编程,不需要main函数
- 默认导入常用的包
- 类不支持default作用域,默认public
- Groovy中基本类型也是对象,可以直接调用对象方法
- 支持DSL和其他简介的语法,让代码易于阅读和维护
- Groovy是基于Java语言的,所以完全兼容Java语法,所以对Java程序员的学习成本较低。
安装
下载后解压,配置到GROOVY_HOME,将bin目录配置到path,groovy -v
检查是否安装成功
使用
参考官网
- 字符串
- 数据类型
- 权限修饰符
- 集合操作
- 类导入
- 异常处理
- 闭包
IDEA创建Gradle项目
选择本地安装的gradle,idea中gradle刷新和terminal中gradlew使用的源可能不一样
创建普通java工程
创建项目时可以选择本地gradle(不会自动生成gradle/wrapper目录了),也可以选择wrapper形式(idea2023.1.4支持到gradle8.0)
创建完项目后可以在File | Settings | Build, Execution, Deployment | Build Tools | Gradle
中手动调整为使用本地gradle
创建web工程
- 调整plugins中id,添加
id war
- 添加依赖
- 在main下添加webapp目录
gretty部署项目
- 将java项目打成war包之后,就需要部署到服务器运行,
- 部署到本地tomcat
- 使用gretty插件中内置服务器方式部署项目
gretty是一个功能丰富的gradle插件,用于在嵌入的servlet容器上运行web应用程序,让项目开发和部署更加简单,目前gretty插件已经作为gradle的核心库使用了,其核心功能为支持jetty、tomcat等sevlet容器,支持项目热部署、https、调试
gretty官网:https://akhikhl.github.io/gretty-doc/index.html
使用:
- 引入gretty插件,
id 'org.gretty' version '2.2.0'
- 指定maven仓库,
jcenter()
,mavenCentral()
- 对插件进行配置
- 执行gretty插件,
gradle appRun
Gradle对测试的支持
测试任务自动检测并执行所有单元测试,最终生成一个报告,支持junit和testNG
使用:
- 导入依赖
- 如果使用junit5,需要使用
useJUnitPlatform()
,也可以进行其他配置 - 写单元测试
- 执行build
Gradle高级
项目的生命周期
Initialization -> Configuration -> Execution
在最后的execution阶段执行由task组成的有向无环树
settings文件
- 作用:主要是在项目初始化阶段确定引入哪些工程,为构建项目工程树做准备
- 工程树:类似于maven中的project和module
- 定义了当前gradle项目及子项目的项目名称
- 必须放在根工程目录下,名字为settings.gradle不能修改
- 对org.gradle.api.initialization.Settings实例是对应关系,每个项目只有一个settings文件
- 作为开发者只需要关注include方法即可,使用相对路径
:
引入子工程,一个子工程只有在settings文件中配置了才能被gradle识别,在构建的时候才会包含进去 - 项目名称中
:
代表项目的分隔符,类似路径中的/,以:
开头表示相对于root project,gradle会为每个带有build.gradle脚本文件的工程构建一个Project对象
Task
项目实质上是Task对象的集合,一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源码、拷贝文件、打包jar文件,甚至是执行一个系统命令。一个Task也可以读取和设置Project的Property以完成特定的操作。
//task: gradle -i task1进行执行 先进行配置阶段、再进行执行阶段
//在不引起歧义的情况下 方法小括号可以省略 参数的引号也可以省略
task task1 { //闭包作为最后一个参数可以写在外面
//任务的配置段 在项目生命周期的配置阶段执行
println "task1 configuration"
//任务的行为 在项目的执行阶段执行 doFirst会在doLast之前执行
doFirst {
println "task1 doFirst"
}
doLast {
println "task1 doLast"
}
}
任务的行为
行为也可以在任务外定义,在执行时,先执行doFirst out,再执行doFirst,再执行doLast,再执行doLast out(这里定义了多个doXxx,放入了列表中,doFirst放入开头,doLast放入结尾)
task1.doFirst {
println "task1 doFirst out"
}
task1.doLast {
println "task1 doLast out"
}
定义task时也可以传入map参数,定义任务自身的action,map中放入action作为key,闭包作为value,那么这个闭包会在doFirst和doLast之间执行
原理:action实际上是一个列表,每次添加一个doFirst都会添加到列表的开头,每次添加一个doLast都会添加到列表的结尾,
任务的依赖方式
task之间的依赖关系可以在以下几部分进行设置,1、参数依赖,2、内部依赖,3、外部依赖
- 参数依赖:在参数上指明依赖的任务列表,使用
dependsOn: ['task1', 'task2']
- 内部依赖:在task内部,为dependsOn属性赋值,
dependsOn = ['task1', 'task2']
- 外部依赖:在任务外动态赋值dependsOn属性,
task5.dependsOn = ['task1', 'task2']
也可以跨项目依赖任务,在写依赖时使用相对目录[":gradle-sub1:sub1_task1"]
任务执行
执行任务:gradle task_name
,或在idea的gradle窗口双击,详细命令参考官网
- 常见的任务
gradle build
,构建项目:编译、测试、打包等操作gradle run
,运行一个服务,需要添加application插件,并指定主启动类gradle clean
,用于清理当前项目下的build目录gradle init
,初始化gradle项目使用gradle wrapper
,生成wrapper文件夹- 升级gradle版本号:
gradle wrapper --gradle-version=x.x
- 关联源码:
gradle wrapper --gradle-version x.x --distribution-type all
- 升级gradle版本号:
- 项目报告相关任务
gradle project
,列出所选项目及子项目列表,以层次结构显示gradle tasks
,列出当前项目的已分配给任务组的taskgradle tasks --all
,列出所选项目的所有任务gradle tasks --group="group_name"
,列出所选项目指定分组的任务gradle help --task task_name
,显示某个任务的详细信息gradle dependencies
,查看某个项目的依赖信息,以依赖树的方式显示gradle properties
,列出所选项目的属性列表
- 调试相关
-h, --help
,查看帮助信息-v, --version
,打印gradle、groovy、ant、jvm和操作系统版本信息-S, --full-stacktrace
,打印出所有的异常的完整堆栈信息-s, --stacktrace
,打印出用户异常的堆栈信息-Dorg.gradle.daemon.debug=true
,调试gradle守护进程-Dorg.gradle.debug=true
,调试gradle客户端-Dorg.gradle.debug.port=5005
,指定调试时要监听的端口号,默认5005
- 性能相关,可以在gradle.properties中指定这些选项中的许多选项,可以不用命令行
--build-cache, --no-build-cache
,尝试重用之前版本的输出,默认关闭off--max-workers
,设置gradle可以使用的worker数,默认是处理器数--parallel, --no-parallel
,并行执行,关于此选项的限制,参考相关文档,默认关闭off
- 守护进程选项
-daemon, no-daemon
,使用gradle守护进程进行构建,默认是on--foreground
,在前台进程中启动gradle进程-Dorg.gradle.daemon.idletimeout=xxx(ms)
,默认3小时,在这个时间后停止自己gradle --stop
,停止守护进程
- 日志选项
-Dorg.gradle.logging.level=(quiet/warn/lifecycle/info/debug)
,通过gradle属性设置日志级别-q, --quiet
,只记录错误信息-w, --warn
,设置日志级别为warn-i, --info
,设置日志级别为info-d, --debug
,调试模式,包括正常的堆栈跟踪
- 其他
-x, --exclude-task
,常见gradle -x test clean build
跳过测试执行--rerun-tasks
,强制执行任务,忽略up-to-date,常见gradle build --rerun-tasks
--continue
,忽略前面失败的任务继续执行,而不是遇到错误后停止,每个遇到的故障都将在构建结束后报告,常见gradle build --continue
gradle init --type pom
,将maven项目转换为gradle项目(根目录执行)gradle task_name
,执行自定义任务
其他:
- 任务名支持缩写,如connectTask,可以使用
gradle cT
,使用每个单词的首字母 - gradle指令的本质是一个个task,gradle的所有操作都是基于task完成的
任务定义方式
一是通过Project的task方法,另一种则是通过tasks对象的create()或register()方法,通过register创建是延迟创建,只有当task被需要使用的时候才会被创建。
在定义任务时也可以指定任务的属性:
任务类型
默认定义的task都是DefaultTask类型的,gradle提供了现有的任务类型帮助我们快速完成想要的任务,只需要在创建任务的时候指定当前任务的任务类型即可,然后就可以使用这种类型中的属性和方法了。可参考官网
也可以自定义类型,继承DefaultTask
任务的执行顺序
gradle中有三种方式可以指定task的执行顺序:
- dependsOn强依赖方式
- 通过task输入输出
- api指定
参考官网
动态分配任务
gradle的强大不仅仅用于定义任务功能,可以使用它在循环中注册同一类型的多个任务,如:
//动态分配任务
4.times {
tasks.register("timesTask$it") {
println "timesTask[${name}] configuration"
doFirst {
println "I'm timesTask[${name}]"
}
}
}
//使用api强制timesTask0依赖于某些任务
tasks.named('timesTask0') { dependsOn('task1', 'task2')}
任务的关闭和重启
修改task的enabled属性,默认为true
任务的超时
修改timeout属性,参数为Duration,超时会抛出异常,后续任务不会再执行,可以使用--continue
在某些任务失败后继续执行其他任务
任务的查找
可以通过路径或名称查找任务
任务的规则
当执行、依赖一个不存在的任务时,gradle会执行失败,可以通过添加规则让其继续执行。还可以根据不同的规则创建需要的任务。
任务的onlyIf断言
task有一个onlyIf方法,它接收一个闭包参数,如果该闭包返回true就执行任务,否则跳过。这很有用,比如控制程序什么情况下打什么包,什么时候执行单元测试,什么时候执行单元测试不执行网络测试等。
默认任务
将任务放入方法defaultTasks中,执行gradle就会默认执行
Gradle中的文件操作
几种常见的文件操作方式:本地文件、文件集合、文件树、文件拷贝、归档文件
本地文件
使用Project.file()方法(也可以自己new File),通过指定文件的相对路径或绝对路径来对文件操作,相对路径为相对于当前项目的或子项目的目录,返回的就是File对象,可以使用它就像在java中使用一样。
文件集合
使用Project.files()方法,返回的文件集合可以进行加减
文件树
使用Project.fileTree()方法,文件树是有层次结构的文件集合,一个文件树可以代表一个目录结构或一个zip压缩包中的内容结构。文件树是从文件集合继承过来的,所以文件树具有文件集合所有的功能。
文件拷贝
使用Copy这个类型的task,或者使用Project的copy方法
归档文件
通常一个项目有很多的jar包,我们希望把项目打包成一个war/zip/tar包进行发布,这是我们就用War/Zip/Tar/Ear任务来实现。读取压缩包使用Project.xxxTree()
Gradle中的Dependencies
- 本地依赖:依赖于本地的jar包,具体可通过文件集合、文件树的方式指定
- 项目依赖:依赖于别的project
- 直接依赖:依赖的类型、组名、名称、版本号
依赖下载
使用配置maven仓库地址下载
依赖的类型
详情参考官网
常见的依赖类型:
- compileOnly:编译时需要,打包时不需要,如sevlet-api,类似maven的provided
- runtimeObly:编译时不需要,运行时需要,比如mysql驱动包,取代老版本的runtime
- implementation:编译运行时都需要,取代老版本compile
- testCompileOnly:针对测试的,编译测试时需要,运行时不需要
- testRuntimeOnly:针对测试的,测试运行时需要,编译时不需要,取代老版本的testRuntime
- testImplementation:针对测试的,编译运行都需要,取代老版本的testCompile
注意点:
- Implementation不支持依赖传递,api支持依赖传递,适用于工程有多个模块场景下为了避免重复依赖(引入第三方的会传递依赖),可以使用api,如a依赖于b,b依赖于c,则a implementation b则无法会用模块c的,必须导入模块c,可以使用a api b,那么就可以使用模块c了。
- 如果使用api进行依赖传递,则当底层模块变化时,所有上层模块都要重新编译,速度没有implementation快
依赖冲突及解决方案
gradle默认使用高版本依赖,也可以排除依赖、强制使用某个版本(在版本号后面加两个!,或者使用闭包的形式)、取消依赖传递(只保留模块自己,模块的依赖都排除掉)
可以配置依赖冲突是直接失败
configurations.all {
Configuration configuration ->
//当版本冲突时直接构建失败
configuration.resolutionStrategy.failOnVersionConflict()
}
如果版本中写的是+或latest.integration
,表示使用最新版本,会遍历所有仓库,找到最新的版本。(不建议使用)
Gradle中的插件
官网文档:https://docs.gradle.org/current/userguide/plugin_reference.html
脚本插件
本质就是一个脚本文件,在build.gradle中引入即可
对象插件
实现了org.gradle.api.Plugin
接口的插件,每个java gradle插件都有一个plugin id
内部插件(核心插件):可参考官网
第三方插件:
- 一般需要配置对应的仓库和类路径,buildscript标签要在所以标签之前
- 如果使用传统方式,插件必须已经被托管到了https://plugins.gradle.org 上,就可以不用在buidscript里配置classpath依赖了
用户自定义插件
实现Plugin接口,泛型为Project,参考官网。只能当前项目使用,其他项目不能使用,局限性强,一般不使用
buildSrc项目
buildSrc是gradle默认的插件目录,编译gradle的时候会自动识别这个目录,将其中的代码编译为插件
创建buildSrc项目,只保留build.gradle和src/main目录,那么这里定义的插件可以被所有工程使用,但是不能被其他工程使用,如果需要别的工程也可以使用,可以单独定义一个插件模块发布到仓库,使用buildScript引入(要放在其他标签之前)
插件的关注点
- 使用:
apply plugin: '插件名'
- 主要的功能,以及插件的添加的任务有哪些:可以通过gradle tasks查看添加了哪些任务
- 项目目录结构
- 依赖管理
- 插件常用的属性
build.gradle文件
- 是一个脚本,支持java/groovy等语言
- 每个project都会有一个build.gradle文件,是项目构建的入口
- 每个build.gradle文件都对应一个Project实例,对文件的设置本质上就是配置Project实例的属性和方法
- root project可以获取到所有的child project,所以可以在root工程的build.gradle文件中对child project做一些统一的配置,如依赖的maven仓库、插件等
- build.gradle常见的属性:
ext定义的属性也可以在任务中、子project中使用
publishing项目发布
- 需要引入maven-publish插件
- 设置相关的发布配置:gav、仓库信息
- 执行相关的发布指令
如果需要发布war包,需要引入war插件,如果需要携带源码和javadoc的发布,需要java-library插件(java插件的升级版)
生命周期中的Hook
是由gradle自动回调完成的
- Initialization阶段
- 先加载初始化脚本
- 执行当前工程的setting.gradle文件
- 执行钩子函数
gradle.settingsEvaluated
- 根据当前项目及子项目名字构建Project实例
- 执行钩子函数
gradle.projectsLoaded
- 配置阶段
- 执行当前工程钩子函数
project.beforeEvaluate/gradle.beforeProject
- 执行当前工程build.gradle及其中声明的task的配置段
- 执行当前工程钩子函数
project.afterEvaluate/gradle.afterProject
- 再执行各个子工程的...
- 所有项目执行完之后,执行钩子函数
gradle.projectsEvaluated
- task依赖关系图建立完毕后会执行钩子函数
gradle.taskGraph.whenReady
- 执行当前工程钩子函数
- 执行阶段
- 根据有向无环图依次执行每个task,其中:
- 先执行钩子函数
gradle.taskGraph.beforeTask
- 再执行task中的before actions
- 再执行task中的after actions
- 再执行钩子函数
gradle.taskGraph.afterTask
- 所以task执行完毕后,再执行钩子函数
gradle.buildFinished
gradle脚本文件执行过程中会生成对应的实例,主要有以下几种对象:
- Gradle对象,在项目初始化时构建,全局单例存在,只有一个对象实例
- Project对象,每一个build.gradle文件都会转换为一个Project对象实例,类似于maven项目中的pom文件
- Settings对象,setting.gradle会转换为一个settings对象,和整个项目是一对一的关系,一般只是用include方法包含子工程
- Task对象,每个build.gradle中可以定义task,gradle是基于task的,一个项目可以有一个或多个task
创建springboot项目
getting started:https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started
springboot-gradle插件:https://docs.spring.io/spring-boot/docs/3.1.3/gradle-plugin/reference/htmlsingle/#introduction
- 导入插件
- 导入依赖
本文来自博客园,作者:Bingmous,转载请注明原文链接:https://www.cnblogs.com/bingmous/p/17655329.html