Gradle之基本理论与冲突解决
上一节,我们通过快速尝鲜领略了gradle的基本功能,现在,学习一下理论吧!
1 构建脚本概要
一、构建脚本概要
gradle构建中的两个基本概念是项目(project)和任务(task),每个构建至少包含一个项目,项目中包含一个或者多个任务。在多项目构建中,一个项目可以依赖于其他项目;类似的,任务可以形成一个依赖关系图来确保他们的执行顺序。
二、项目-任务的关系
如图,项目一个项目可以依赖另一个项目,一个任务可以依赖另一个任务。
三、项目介绍
一个项目代表一个正在构建的组件(比如一个jar文件),当构建启动后,Gradle会基于build实例化一个ora.gradle.api.Project
类,并且能够通过project变量使其隐式可用,即builde.gradle里面的version
等价于project.version
。
1)通过属性group(groupId)、name(artifactId)、version确定一个唯一组件
2)project的重要的方法:
-apply 应用插件
-dependencies 添加依赖
-repositories 仓库找jar
-task 声明项目有什么任务
3)属性的其他配置方式:ext、gradle.properties
plugins {
id 'java'
id 'war'
}
// 点入可以看到调用Project类的setGroup方法
project.group 'com.sef'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
四、任务介绍
任务对应org.gradle.api.Task
。主要包括任务动作和任务依赖。任务动作定义了一个最小的工作单元。可以定义依赖于其他任务、动作序列和执行条件。
1)task的重要方法:
-dependsOn 依赖task
-doFirst、doLast 在(前面、后面)添加动作
我们使用插件以后,默认会带很多任务。
如图,当我们执行类jar任务后,在它前面执行了多个任务,complie、process、classes等。
2 自定义任务
在build.gradle里面添加一个任务:
def createDir = {
path ->
File dir = new File(path);
if(!dir.exists()){
dir.mkdirs();
}
}
// 创建java工程目录
task makejavaDir(){
def paths = ['src/main/java', 'src/main/resources','src/test/java','src/test/resources']
doFirst{
paths.forEach(createDir)
}
}
// 创建web工程目录
task makeWebDir(){
dependsOn 'makejavaDir'
def paths = ['src/main/webapp','src/test/webapp']
doFirst{
paths.forEach(createDir)
}
}
定义一个任务makejavaDir,用于创建java工程目录,定义任务makeWebDir,用于创建Web工程目录;
任务makeWebDir依赖于makejavaDir任务;
可以看到任务列表other里面多了两个任务;
执行任务可以创建对应的目录;
3 构建生命周期
一、生命周期
初始化阶段
Gradle提供了单项目和多项目的构建,在初始化阶段,他要决定哪些项目要参与到构建中来,并创建这些项目的实例。初始化阶段的任务是创立项目的层次结构,并且为每一个项目创立一个
Project
实例。 与初始化阶段相关的脚本文件是settings.gradle
(包括<USER_HOME>/.gradle/init.d
目录下的所有.gradle脚本文件,这些文件作使用于本机的所有构建过程)配置阶段
在这个阶段需要参与到本次构建的项目的构建脚本将会被执行,这些项目的对象将被配置,这个特性叫做configuration on demand
配置阶段的任务是执行各项目下的
build.gradle
脚本,完成Project的配置,并且构造Task
任务依赖关系图以便在执行阶段按照依赖关系执行Task
。 该阶段也是我们最常接触到的构建阶段,比方应使用外部构建插件apply plugin: com.android.application
,配置插件的属性android{ compileSdkVersion 25 ...}
等。每个build.gralde
脚本文件对应一个Project
对象,在初始化阶段创立,Project
的接口文档。 配置阶段执行的代码包括build.gralde
中的各种语句、闭包以及Task
中的配置段语句执行阶段
Gradle为在配置阶段创建和配置的任务再次筛选需要执行的任务的子集,这个子集的筛选是由在命令行下传递给Gradle的任务名和目录决定的,子集中的每个任务都会被执行。在配置阶段结束后,Gradle会根据任务Task的依赖关系创立一个有向无环图,能通过
Gradle
对象的getTaskGraph
方法访问,对应的类为TaskExecutionGraph,而后通过调使用gradle <任务名>
执行对应任务。如:
二、钩子
当然,构建生命周期中还有钩子方法,它们的执行时机如下图:
构建生命周期有一些钩子方法,我们可以查阅官方文档,了解钩子方法是怎么使用的。
4 依赖管理
一、概要
几乎所有的基于JVM的软件项目都需要依赖外部类库来重用现有的功能。自动化的依赖管理可以明确依赖的版本,可以解决因传递性依赖带来的版本冲突。
二、基本概念
工件坐标
group、name、version
通过这三个参数可以唯一确定一个jar
仓库
用来存放jar的地方。
本地仓库:mavenLocal
公用仓库:MavenCentral、jcenter(公网上的)
文件仓库:本地机器上的jar(不推荐使用)
在build.gradle中配置按照如下顺序配置:
repositories { // 私服 //maven{ // url '' //} // 本地仓库(若是我们按照了maven) mavenLocal() // 公共仓库 mavenCentral() }
依赖的传递性
正是因为依赖的传递性,导致版本冲突。如,B依赖A,如果C依赖B,那么C依赖A。
三、自动化依赖管理
下图显示了依赖管理的关键元素:
配置包含两部分:依赖的标识加版本号和中央仓库的位置(可以是一个HTTP链接),依赖管理器根据这个信息自动定位到需要下载的仓库然后下载到你的机器中。库可以定义传递依赖,依赖管理器足够聪明分析这个信息然后解析下载传递依赖。如果出现了依赖冲突,依赖管理器会试着解决。库一旦被下载就会存储在本地的缓存中,构建系统先检查本地缓存中是否存在需要的库然后再从远程仓库中下载。
四、依赖管理阶段
源代码有compile、runtime;测试代码有testCompile、testRuntime,阶段之间的关系如下图:
1)运行阶段依赖编译阶段;
2)测试的阶段依赖于源代码的阶段;
dependencies {
// 有两种写法
// 添加源代码编译时依赖
compile 'ch.qos.logback:logback-examples:1.3.0-alpha4'
// 添加测试代码编译时依赖
testCompile group: 'junit', name: 'junit', version: '4.12'
}
如下:
现在,我们了解了基本原理,上面说依赖的传递性会造成版本冲突问题,那么版本冲突如何解决?干货就在下篇。一鼓作气,接下去。