Gradle
官方文档:https://docs.gradle.org/current/userguide/userguide.html
Java三种构建工具比较
对比 | Ant | Maven | Gradle |
---|---|---|---|
构建性能 | 最高 | 最低 | 居中 |
仓库 | 开发者自己处理 | maven仓库 | 支持各种仓库 |
依赖管理 | ivy管理 | GAV管理 | GNV管理 |
插件支持 | 实现方便 | 实现较难 | 实现方便 |
遵循特定的目录结构 | No | Yes | Yes |
配置文件 | xml文件(繁琐) | xml文件 | 代码脚本,便于写业务逻辑 |
侧重点 | 小型项目构建 | 项目包管理 | 大型项目构建 |
目前地位 | 使用较少 | 目前主流 | 未来趋势 |
项目的目录结构
Gradle指令
指令 | 作用 |
---|---|
| 创建gradle项目 |
| 将maven项目转换为gradle |
| 编译源代码与配置文件 |
| 编译测试代码,生成测试报告 |
| 构建项目 |
| 构建项目,且跳过测试 |
| 清空build目录 |
init.gradle
~/.gradle/init.gradle
是gradle的全局配置脚本,他有四种方式可以配置,优先级从高到低:
运行指令时指定初始化脚本:
gradle -init-script yourdir/init.gradle -q taskName
,也可以指定多个init文件$USER_HOME/.gradle/init.gradle
文件$USER_HOME/.gradle/init.d/*.gradle
多个文件$GRADLE_HOME/init.d/*.gradle
多个文件
gradle会按照上述1-4的顺序依次执行这些文件,如果文件夹中包含多个脚本文件,则会按照字母顺序依次执行。
仓库说明
配置远程仓库地址:
Gradle下载的依赖的位置位于$USER_HOME/.gradle/caches
,如果配置了$GRADLE_USER_HOME
环境变量,将会放在该目录下的caches
子目录下。
注意,不要将caches目录与MVN的本地仓库指向统一位置,因为caches下载文件与mvn仓库的存放方式不同。
Gradle Wrapper
Gradle Wrapper是对Gradle的一层包装,用于解决实际开发中不同项目需要不同版本的Gradle问题:
项目拉取者本地没有Gradle
项目拉去这本地Gradle版本与项目不一致
这个时候就需要gradle了。
Gradle Wrapper 提供了如下文件:
使用gradle修改gradle wrapper的信息
参数 | 说明 |
---|---|
| 用于指定使用Gradle版本 |
| 用于指定下载gradle发行版本的url(原来的可能很慢) |
直接修改配置文件修改gradle wrapper的信息
修改properties 文件即可:
Gradle Wrapper的执行流程
执行
./gradlew build
读取
./gradle/gradle-wrapper.properties
配置文件将配置文件中的指定版本的gradle下载到
$GRADLE_HOME/wrapper/dists
目录下(如果已经有了将不会再下载了)并构建本地缓存(
$GRADLE_HOME/caches
)之后执行的所有的
.gradlew
命令实际都是调用的目标版本的gradle命令
Gradle的生命周期
Gradle项目的生命周期分为三大阶段:
Initialization 初始化
执行初始化脚本(
init.d/*.gradle
)执行
settings.gradle
,主要包含当前项目声明以及当前项目子模块声明生成项目工程树
Configuration 配置
根据初始化阶段得到的项目工程树
依次调用每个模块的
build.gradle
脚本调用顺序为先加载跟工程的,再加载所有的二级子工程的脚本,然后是三级子工程所有...
加载的
build.gradle
脚本最终会生成一个由task组成的有向无环数
Execution 执行
根据有向无环数依次执行task,完成构建
settings.gradle
主要是在项目初始化阶段为项目工程树做准备:
位于项目根路径
名称必须为
settings.gradle
定义了当前gradle项目以及子项目的名称
每个项目只能有一个
settings.gradle
文件,他在gradle内部会生成一个org.gradle.api.initialization.Settins
实例是gradle的识别标志,没有该文件不会认为该项目是一个gradle项目
使用
inclue('路径')
引入项目模块
在gradle中,
:
可以试做/
,如果以:
开头,代表是根目录开始。
build.gradle
build.gradle
是一个gradle构建脚本,支持java、groovy语法每个project实例对应一个
build.gradle
脚本,文件中的所有方法调用都是调用的project对象的实例方法。RootProject也可以拥有一个
build.gradle
脚本文件,通常用来设置子project的一些公共信息,比如仓库、所有应用依赖的插件、所有应用的依赖项
下面是一个标准的 build.gradle
:
属性:项目坐标 group name version
自带属性:group,name,version 对应maven的GAV,用于表示项目的坐标。其中,group与version信息配置在build.gradle
文件中,而name则是配置在settings.gradle
文件中。
build.gradle:
settings.gradle:
属性:自定义属性信息 ext
可以使用ext方法定义自定义属性,并引用:
ext信息不仅可以放在build.gradle中,还可以放在任务中:
方法:repository
定义仓库,gradle在下载依赖的时候,会从上到下依次从每个仓库中寻找需要的依赖。如果找到将会停止向下搜索,如果找不到则继续向下查找。
方法:dependencies
在gradle中,依赖有三种方式:
本地依赖,依赖本地的某个jar,可以通过文件集合、文件树的方式指定
项目依赖,依赖某个定义在
settings.gradle
的project直接依赖,
依赖类型 依赖组名 依赖名称 依赖版本号
依赖的下载
当执行build命令时,gradle就会去配置的依赖仓库中下载对应的jar,并应用到项目中。
依赖的类型
相当于maven的scope:
依赖类型 | 描述 | 示例 |
---|---|---|
| 由java插件提供,适用于编译器需要,但是打包不需要的情况 | servlet-api容器已经提供 |
| 由java插件提供,只在运行期有效,编译器不需要 | mysql的驱动包,编译期间不需要知道驱动包的细节,但是运行时必须有 |
| 由java插件提供,针对源码目录,在编译运行都有效 | 比如commons-lang、spring等 |
| 由java插件提供,用于编译测试的依赖项 | |
| 由java插件提供,用于运行时需要的依赖项,编译不需要 | |
| 由java插件提供,测试编译、运行期间都需要 | |
| 由java-library插件提供,与implementation基本一致,但是提供了依赖传递的效果 | |
| 由java-library插件提供,在编译器有效,运行期无效,其他与api一致 |
api
与implementation
的区别,假设有四个模块:
A implementation B, B implementation C, A模块编译期间无法使用C中的类
A implementation B, B api C,则A可以在编译期间使用C种的类
A implementation B, B implementation C, C api D,则B编译期间可以使用A种的类,但是A不能使用B中的类
A implementation B, B api C, C api D, 这样可以使用D
总的来说,api标记了这个依赖项是否可以被需要依赖当前项目的人使用;而implementation则代表当前依赖项仅供当前项目使用,不能被其他需要依赖该项目的项目使用。因为api的模块需要暴露给别的模块使用,将会影响编译速度。在开发过程中,优先选择implementation
,如果涉及到多模块的依赖,再考虑使用api。注意当项目中拥有大量的api依赖项会显著增加构建时间。
高版本gradle已经废弃了 api,使用implementation即可
依赖冲突的解决
在编译期间,如果系统中出现同一依赖的不同版本,构建系统应该选择什么版本进行构建的问题:
项目依赖A模块,A模块依赖了log4j1.0
项目还依赖了B模块,B模块依赖了log4j2.0
编译期间不知道该使用log4j1.0还是log4j2.0
默认情况下,Gradle会使用最新的jar,比如上述案例就会使用log4j2.0的jar,自动剔除1.0的jar。
如果不想遵循默认的规则,可以通过exclude
手动排除某个冲突的jar:
或者禁用引用包的依赖传递,这样打包时也可以提出某个依赖(不建议使用):
或者强制使用某个版本,让项目目中所有涉及当前依赖的模块都是用这个版本:
使用依赖最新版本
不建议使用。
方法:allprojects、subprojects、project
主要解决问题:如果在某个build.gradle
中定义一些配置以及依赖,那么这些配置依赖只会对当前项目有效,他的子项目不会生效。
allprojects 设置当前工程以及当前工程的所有子工程一起设置,通常放在rootproject/build.gradle
中:
而subprojects是对当前工程下的所有子工程设置,当前工程下不设置:
subprojects和allprojects可以为项目设置公用的插件、依赖、属性,此外,还可以使用project方法,对某个子工程进行一些独立的设置:
注意:
subproject1 名字定义在了
settings.gralde
中在allprojects、subprojects、project不支持语法
plugins {}
应用插件,需要使用apply plugin: 'xx'
替换
方法:sourceSets
设置项目源集,比如java的默认源码目录src/main/java
,以及构建结果目录。
方法:apply、plugins
前两者是旧的插件依赖方式,plugins则是新的插件依赖方式。
方法:buildScript
buildscript里是gradle脚本执行所需依赖,分别是对应的maven库和插件:
buildScript必须在
build.gradle
文件的最顶端,因为需要为执行脚本做准备对于多项目构建,项目的 buildScript 方法声明的依赖关系可用于其所有子项目的构建脚本,类似allprojects、subprojects
这里定义的依赖可能是普通依赖,也可能是插件依赖
gradle.properties
Task
Project实质上是Task对象的集合。一个Task表示一个逻辑较为独立的执行过程,比如编译、文件拷贝、打包等,甚至是一个简单的系统命令。一个Task可以读取和设置Project的Property以完成特定的操作。
定义Task
在 build.gradle
中定义一个简单的Task:
执行task:
Task的执行原理
task中的所有行为是一个action实例,这些实例会放入到一个列表中,列表的最前面是一个或者多个doFirstAction
,最后面则是一个或者多个doLastAction
。先添加的将会放在列表的前面。
指定Task的属性
配置项 | 描述 | 默认值 |
---|---|---|
type | 指定父task,基于另一个task创建新的task | DefaultTask |
overwrite | 是否替换存在的Task,这个和type配合使用 | false |
dependsOn | 配置任务的依赖关系 | [] |
action | 添加到任务中的Action或者一个闭包 | null |
description | 任务描述 | null |
group | 任务分组,比如 | null |
示例:
type
默认的task继承自DefaultTask类型,如果要完成某些具体的操作,完全需要我们自己去编写gradle脚本,这样肯定有些麻烦,所以提供了type属性去继承某个task,复用他的一些逻辑。官方为我们提供了一些默认的TaskType使用:
常见的任务类型 | 作用 |
---|---|
Delete | 删除文件或目录 |
Copy | 将文件复制到目标目录中,还可以在复制时重命名和筛选文件 |
CreateStartScripts | 创建启动脚本 |
Exec | 执行命令进程 |
GenerateMavenPom | 生成Maven Pom文件 |
GradleBuild | 执行Gradle构建 |
Jar | 组装Jar归档文件 |
JavaCompiler | 编译Java源码 |
Javadoc | 为Java代码生成HTML API |
PublishToMavenRepository | 推送到远程maven仓库 |
Tar | 组装tar归档文件 |
Test | 执行Junint或者TestNG测试 |
Upload | 将Configuration的构建上传到一组存储库 |
War | 组装war归档文件 |
Zip | 组装Zip归档文件 |
示例(删除构建目录):
或者使用类继承的方式:
dependsOn
build.gradle
可以定义多个task,多个task可能存存在依赖关系:
任务的执行顺序
有三种方式控制任务的执行顺序:
dependsOn 强依赖方式(不推荐)
通过 Task 输入输出(不推荐)
通过API指定执行顺序
动态分配任务
任务关闭与开启
任务的超时
可以为任务设置超时时间,如果大于超时时间,则会抛出一个异常,并且中断后面的任务执行:
任务的定位和查找
任务的onlyIf断言
只有满足目标条件的时候,任务才会执行,否则将会跳过。
输出结果:
操作文件
拷贝文件,常产用于生成构建内容。下面是通过CopyTask的方式:
project也提供了相应的copy方法:
文件归档:
默认任务
默认情况下,如果要执行某个任务,需要通过gradle -q taskName
命令指定任务名称,然后才可以执行任务。如果想通过gradle
直接执行某些任务,需要将任务设置为默认的任务:
运行gradle命令,将会显示:
Plugin
在项目中引用插件,可以:
添加插件定义好的task到项目中,帮助我们完成编译、测试、打包
添加一些新的依赖类型到项目中
向项目中扩展一些新的属性以及方法,比如加入插件之后,可以设置jdk的版本信息
插件可以对项目进行一些约定俗称的配置
Gradle插件分类:
脚本插件
二进制插件(对象插件)
内部插件
第三方插件
自定义插件
脚本插件
脚本插件是在项目中定义.gradle
结尾的gradle脚本文件,然后引入到build.gradle
中,比如下面定义了一个version.gradle
的脚本,记录了当前java的版本信息:
在build.gradle
引如这个脚本,并创建一个Task使用脚本中生命的变量:
对象插件(二进制插件)
所谓对象插件就是实现 org.gradle.api.Plugin
接口的插件,每一个插件都有且必须有一个 plugin id。可以通过apply使用插件:
内部插件
内部也是核心插件,可以在 https://docs.gradle.org/current/userguide/plugin_reference.html查看所有的核心插件。
使用核心插件:
或者使用apply:
第三方插件
不是gradle自带的,而是由其他第三方提供的插件,一般需要配置对应的仓库和类路径,否则找不到插件:
如果插件已经被托管到 https://plugins.gradle.org 中,可以使用如下方式直接引入插件:
自定义插件
通过实现Plugin<Project>
接口创建一个project的插件,一个插件中可能包含多个task:
上述方式只支持在一个模块中使用插件,如果想要多个模块之间共享插件,可以使用buildSrc的方式:
buildSrc
是Gradle的默认插件目录,编译插件时会自动识别这个目录,将其编译为插件。先在根项目目录建立一个名称为
buildSrc
的gradle模块,并且不在settings.gradle
中include这个模块,只保留build.gradle
以及src/main
。修改
build.gradle
如果多个项目之间需要使用同一个插件,我们将上述做好的插件单独抽成一个插件项目并将依赖包上传到maven私服中即可。如果要使用仓库,项目只需要依赖这个插件依赖,并apply即可:
使用 maven-publish 插件提供的task发布即可。
发布插件的官方文档:https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html
三方插件关注点
如何引入插件
插件所包含的主要task
插件约定的工程结构(约定优于配置)
插件提供的依赖管理以及依赖的类型、
插件提供的额外的属性,可以再build.gradle中使用
Java插件示例,参考官方文档: https://docs.gradle.org/current/userguide/java_plugin.html#java_plugin
最后更新于