1 Gradle
1.1 介绍
gradle 和maven 一样都是用来构建java 程序的,maven2004 年开始兴起,gradle2012 年开始诞生,既然已经有了mave n这么成熟的构建工具为什么还有gradle 的诞生呢,因为gradle 有很多地方比maven 做的更好,例如gradle 采用groovy 语言开发,语法更加简单,例如maven 一个配置需要三行,而gradle 只需要一行即可
Gradle 是继Maven 之后的新一代构建工具,它采用基于groovy 的DSL 语言作为脚本,相比传统构建工具通过XML 来配置而言,最直观上的感受就是脚本更加的简洁、优雅。如果之前对Maven 有所了解,那么可以很轻易的转换到Gradle ,它采用了同Maven 一致的目录结构,可以与Maven 一样使用Maven 中央仓库以及各类仓库的资源,并且Gradle 默认也内置了脚本转换命令可以方便的将POM 转换为gradle.build
1.2 优势
- 依赖管理:即将项目中的jar包管理起来,可以使用
Maven 或者Ivy 的远程仓库、或者本地文件系统等 - 编译打包:可以通过脚本实现花样打包,包括修改文件、添加抑或排除某些类或资源、采用指定
JDK 版本构建、打包后自动上传等等等等 - 多项目支持:
Gradle 对多项目有着良好的支持,比如一个很具有代表性的实践就是spring framework - 多语言支持:无论是java、groovy、scala、c++都有良好的支持
- 跨平台支持:gradle是基于jvm的,只要有jvm就可以让gradle运行
- 灵活的的脚本:可以使用
groovy 灵活的编写任务完成你想要做的任何事情
1.2.1 约定优于配置
约定优于配置(convention over configuration ),简单而言就是遵循一定的固定规则从而可以避免额外的配置。虽然这一定程度上降低了灵活性,但却能减少重复的额外配置,同时也可以帮助开发人员遵守一定的规则。当然,约定并不是强制性约束,Gradle提供了各种灵活的途径可以更改默认的配置
1.2.2 结构
- 标准结构
Gradle 遵循COC (convention over configuration 约定优于配置)的理念,默认情况下提供了与maven 相同的项目结构配置 大体结构如下
project root src/main/java(测试) src/main/resources src/test/java(测试源码目录) src/test/resources(测试资源目录) src/main/webapp(web工程)
- 非标准结构配置
在一些老项目上,可能目录结构并不是标准结构,然而一般开发人员又不好进行结构调整.此时可以通过配置sourceSet 来指定目录结构
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'src/resources'
}
}
}
或者如下写法:
sourceSets {
main.java.srcDirs = ['src/java']
main.resources.srcDirs = ['src/resources']
}
- 搭配include和exclude进行指定
sourceSets {
main {
java {
}
resources {
srcDir 'main/resources'
include '**/*.properties'
include '**/*.png'
srcDir 'src'
include '**/Messages*.properties'
exclude '**/*.java'
}
}
}
1.2.3 脚本
一个简单的Gralde 脚本,或许包含如下内容,其中标明可选的都是可以删掉的部分:
- 插件引入:声明你所需的插件
- 属性定义(可选):定义扩展属性
- 局部变量(可选):定义局部变量
- 属性修改(可选):指定project自带属性
- 仓库定义:指明要从哪个仓库下载jar包
- 依赖声明:声明项目中需要哪些依赖
- 自定义任务(可选):自定义一些任务
buildScript {
repositories {
mavenCentral()
}
}
apply plugin: 'java'
ext {
foo="foo"
}
def bar="bar"
group 'pkaq'
version '1.0-SNAPSHOT'
repositories {
jcenter()
}
dependencies {
compile "cn.pkaq:ptj.tiger:+"
}
task printFoobar {
println "${foo}__${bar}"
}
1.3 依赖管理
通常而言,依赖管理包括两部分,对依赖的管理以及发布物的管理;依赖是指构建项目所需的构件(jar 包等)。例如,对于一个应用了spring 普通的java web 项目而言,spring 相关jar 包即项目所需的依赖。发布物,则是指项目产出的需要上传的项目产物
1.3.1 采用变量统一控制版本号
dependencies {
def bootVersion = "1.3.5.RELEASE"
compile "org.springframework.boot:spring-boot-starter-web:${bootVersion}",
"org.springframework.boot:spring-boot-starter-data-jpa:${bootVersion}",
"org.springframework.boot:spring-boot-starter-tomcat:${bootVersion}"
}
1.3.2 自动获取最新版本依赖
如果你想某个库每次构建时都检查是否有新版本,那么可以采用+ 来让Gradle 在每次构建时都检查并应用最新版本的依赖。当然也可以采用1.x,2.x 的方式来获取某个大版本下的最新版本。
dependencies {
compile "org.springframework.boot:spring-boot-starter-web:+"
}
1.3.3 依赖的坐标
仓库中构件(jar包 )的坐标是由configurationName group:name:version:classifier@extension 组成的字符串构成,如同Maven中的GAV 坐标,Gradle 可借由此来定位想搜寻的jar包 。
在gradle中可以通过以下方式来声明依赖:
testCompile group: 'junit', name: 'junit', version: '4.0'
项目 | 描述 |
---|
configurationName | 依赖的作用范围 | group | 通常用来描述组织、公司、团队或者其它有象征代表意义的名字,比如阿里就是com.alibaba,一个group下一般会有多个artifact | name | 依赖的名称,或者更直接来讲叫包名、模块、构件名、发布物名以及随便怎么称呼。druid就是com.alibaba下的一个连接池库的名称 | version | 见名知意,无它,版本号 | classifier | 类库版本,在前三项相同的情况下,如果目标依赖还存在对应不同JDK版本的版本,可以通过此属性指明 | extension | 依赖的归档类型,如aar、jar等,默认不指定的话是jar |
由于Gradle 依赖配置支持多种书写方式,采用map 或者字符串
采用map描述依赖
testCompile group: 'junit', name: 'junit', version: '4.0'
采用字符串方式描述依赖
testCompile 'junit:junit:4.0'
显然采用字符串的方式更加简单直观,当然借助groovy 语言强大的GString 还可以对版本号进行抽离。如下面的示例,这里需要注意的是如果要用GString 的话,依赖描述的字符串要用"双引号" 包起来才会生效。
def ver = "4.0"
testCompile "junit:junit:${ver}"
1.3.4 依赖的范围
上面的例子中采用的testComplie 是声明依赖的作用范围,关于各种作用范围的功效可见下表 这里需要注意的是,provided 范围内的传递依赖也不会被打包
名称 | 说明 |
---|
compileOnly | gradle2.12之后版本新添加的,2.12版本时期曾短暂的叫provided,后续版本已经改成了compileOnly,由java插件提供,适用于编译期需要而不需要打包的情况 | providedCompile | war插件提供的范围类型:与compile作用类似,但不会被添加到最终的war包中这是由于编译、测试阶段代码需要依赖此类jar包,而运行阶段容器已经提供了相应的支持,所以无需将这些文件打入到war包中了;例如Servlet API就是一个很明显的例子. | api | 3.4以后由java-library提供,当其他模块依赖于此模块时,此模块使用api声明的依赖包是可以被其他模块使用 | implementation | 3.4以后由java-library提供,当其他模块依赖此模块时,此模块使用implementation声明的依赖包只限于模块内部使用,不允许其他模块使用 | compile | 编译范围依赖在所有的classpath中可用,同时它们也会被打包 | providedRuntime | 同proiveCompile类似 | runtime | runtime依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要JDBC API JAR,而只有在运行的时候才需要JDBC驱动实现。 | testCompile | 测试期编译需要的附加依赖 | testRuntime | 测试运行期需要 | archives | - | default | 配置默认依赖范围 |
1.3.5 依赖的分类
类型 | 描述 |
---|
外部依赖 | 依赖存放于外部仓库中,如jcenter ,mavenCentral等仓库提供的依赖 | 项目依赖 | 依赖于其它项目(模块)的依赖 | 文件依赖 | 依赖存放在本地文件系统中,基于本地文件系统获取依赖 | 内置依赖 | 跟随Gradle发行包或者基于Gradle API的一些依赖,通常在插件开发时使用 | 子模块依赖 | - |
1.3.5.1 外部依赖
可以通过如下方式声明外部依赖,Gradle 支持通过map 方式或者g:a:v 的简写方式传入依赖描述,这些声明依赖会去配置的repository 查找。
dependencies {
compile group: 'commons-lang', name: 'commons-lang', version: '2.6'
compile(
[group: 'org.springframework', name: 'spring-core', version: '2.5'],
[group: 'org.springframework', name: 'spring-aop', version: '2.5']
)
compile 'org.projectlombok:lombok:1.16.10'
compile 'org.springframework:spring-core:2.5',
'org.springframework:spring-aop:2.5'
}
1.3.5.2 项目依赖
此类依赖多见于多模块项目,书写方式如下,其中:是基于跟项目的相对路径描述符。
compile project(':project-foo')
1.3.5.3 文件依赖
依赖存在于本地文件系统中,举个栗子,如oracle 的OJDBC 驱动,中央仓库中没有又没有自建私服此时需要放到项目lib 下进行手工加载那么便可采用此种方式,可以通过FileCollection 接口及其子接口提供的方法加载这些依赖(支持文件通配符)
dependencies {
compile files('hibernate.jar', 'libs/spring.jar')
compile fileTree('libs')
compile fileTree(dir:'libs',include:'spring*.jar',exclude:'hibernate*.jar')
}
1.3.5.4 内置依赖
跟随Gradle 发行包或者基于Gradle API 的一些依赖,通常在插件开发时使用,当前提供了如下三种
dependencies {
compile localGroovy()
compile gradleApi()
/使用 Gradle test-kit API 作为依赖
testCompile gradleTestKit()
}
1.3.5.5 子模块依赖
简单来说就是声明依赖的依赖或者依赖的传递依赖,一般情况下如果依赖的库并未用构建工具构建(尤其是一些上古时代的老库),那么Gradle 是无法透过源文件去查找该库的传递性依赖的,通常而言,一个模块采用XML(POM文件) 来描述库的元数据和它的传递性依赖。Gradle 可以借由此方式提供相同的能力,当然这种方式也会可以改写原有的传递性依赖。这里让druid 连接池依赖了ptj.tiger 的一个库。
dependencies {
compile module("com.alibaba:druid:1.0.26") {
dependency("cn.pkaq:ptj.tiger:+")
}
runtime module("org.codehaus.groovy:groovy:2.4.7") {
dependency("commons-cli:commons-cli:1.0") {
transitive = false
}
module(group: 'org.apache.ant', name: 'ant', version: '1.9.6') {
dependencies "org.apache.ant:ant-launcher:1.9.6@jar",
"org.apache.ant:ant-junit:1.9.6"
}
}
}
1.3.6 传递依赖
在Maven 仓库中,构件通过POM (一种XML 文件)来描述相关信息以及传递性依赖。Gradle 可以通过分析该文件获取获取所有依赖以及依赖的依赖和依赖的依赖的依赖,为了更加直观的表述,可以通过下面的输出 结果了解。
可以看到,我们的项目依赖了com.android.support-v4 包,然而com.android.support-v4 包却依赖了一众support 的全家桶,借助Gradle 的传递性依赖特性,无需在脚本中把这些依赖都声明一遍,只需要简单的一行,Gradle 便会帮你将传递性依赖一起下载下来。
传递依赖特性可以轻松地通过transitive 参数进行开启或关闭,上面的示例中如果要忽略com.android.support-v4 的传递性依赖可以采用指定 transitive = false 的方式来关闭依赖传递特性,也可以采用添加@jar 的方式忽略该依赖的所有传递性依赖。
compile('com.android.support:support-v4:23.1.1'){
transitive = false
}
compile 'com.android.support:support-v4:23.1.1'@jar
当然,也可以全局性的关闭依赖的传递特性。
configurations.all {
transitive = false
}
1.3.7 排除依赖
有些时候可能需要排除一些传递性依赖中的某个模块,此时便不能靠单纯的关闭依赖传递特性来解决了。这时exclude 就该登场了,如果说@jar 彻底的解决了传递问题,那么exclude 则是部分解决了传递问题。然而实际上exclude 肯能还会用的频率更更频繁一些,比如下面几种情况。
可以通过configuration 配置或者在依赖声明时添加exclude 的方式来排除指定的引用。 exclude 可以接收group 和module 两个参数,这两个参数可以单独使用也可以搭配使用,具体理解如下:
compile('com.github.nanchen2251:CompressHelper:1.0.5'){
exclude group: 'com.android.support'
exclude module: 'appcompat-v7'
exclude group: 'com.github.nanchen2251', module: 'CompressHelper'
}
1.3.8 更新依赖
在执行build、compile 等任务时会解析项目配置的依赖并按照配置的仓库去搜寻下载这些依赖。默认情况下,Gradle 会依照Gradle缓存 ->配置的仓库的顺序依次搜寻这些依赖,并且一旦找到就会停止搜索。如果想要忽略本地缓存每次都进行远程检索可以通过在执行命令时添加--refresh-dependencies 参数来强制刷新依赖。
gradle build --refresh-dependencies
当远程仓库上传了相同版本依赖时,有时需要为缓存指定一个时效去检查远程仓库的依赖笨版本,Gradle 提供了cacheChangingModulesFor(int, java.util.concurrent.TimeUnit) ,cacheDynamicVersionsFor(int, java.util.concurrent.TimeUnit) 两个方法来设置缓存的时效
configurations.all {
resolutionStrategy.cacheChangingModulesFor 24, 'hours'
resolutionStrategy.cacheDynamicVersionsFor 10*60, 'seconds'
}
dependencies {
compile group: "group", name: "module", version: "1.1-SNAPSHOT", changing: true
}
1.3.8.1 缓存位置管理
Gradle 在按照配置的仓库去搜寻下载依赖时,下载的依赖默认会缓存到USER_HOME/.gradle/caches 目录下,当然也可以手工修改这个位置。 具体可以参考如下三种方式:
- 通过添加系统变量
GRADLE_USER_HOME - 设置虚拟机参数
org.gradle.user.home 属性 - 通过命令行
-g 或者 --gradle-user-home 参数设置
1.3.8.2 离线模式(总是采用缓存内容)
Gradle 提供了一种离线模式,可以让你构建时总是采用缓存的内容而无需去联网检查,如果你并未采用动态版本特性且可以确保项目中依赖的版本都已经缓存到了本地,这无疑是提高构建速度的一个好选择。开启离线模式只需要在执行命令时候添加--offline 参数即可。当然,采用这种模式的也是有代价的,如果缓存中搜寻不到所需依赖会导致构建失败。
gradle build --offline
1.3.9 依赖-构件的上传与发布
借助maven-publish 插件可以轻松地将jar包发布到仓库中。这个过程没啥幺蛾子直接上代码吧
apply plugin: 'maven-publish'
apply plugin: 'java'
task sourceJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'sources'
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
publishing {
repositories {
maven {
url "xxx"
}
}
publications {
mavenJava(MavenPublication) {
groupId 'org.pkaq'
artifactId 'tiger'
version '1.1'
from components.java
artifact sourceJar
pom.withXml {
Node root = asNode()
root.appendNode('description', 'bazinga!')
}
}
}
}
model {
tasks.generatePomFileForMavenJavaPublication {
destination = file("$buildDir/generated-pom.xml")
}
}
1.3.10 检查依赖
在引用的依赖或传递性依赖存在版本冲突时,Gradle 采用的策略是优先选取最新的依赖版本解决版本冲突问题。解决此类问题我们可以通过依赖管理方法进行排除、强制指定一个版本或者干脆禁用依赖传递特性解决。但如何知道哪些依赖传递了哪些子依赖,哪些传递的依赖又被Gradle 进行了隐性升级呢。采用下面的命令可以查看各个范围的依赖树。
gradle dependencies > dep.log
输出结果:
dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Dependencies for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web:1.4.2.RELEASE
| +--- org.springframework.boot:spring-boot-starter:1.4.2.RELEASE
| | +--- org.springframework.boot:spring-boot:1.4.2.RELEASE
| | | +--- org.springframework:spring-core:4.3.4.RELEASE
| | | | \--- commons-logging:commons-logging:1.2
| | | \--- org.springframework:spring-context:4.3.4.RELEASE
| | | +--- org.springframework:spring-aop:4.3.4.RELEASE
| | | | +--- org.springframework:spring-beans:4.3.4.RELEASE
| | | | | \--- org.springframework:spring-core:4.3.4.RELEASE (*)
| | | | \--- org.springframework:spring-core:4.3.4.RELEASE (*)
| | | +--- org.springframework:spring-beans:4.3.4.RELEASE (*)
| | | +--- org.springframework:spring-core:4.3.4.RELEASE (*)
| | | \--- org.springframework:spring-expression:4.3.4.RELEASE
....
....
省略的
....
....
\--- org.apache.tomcat.embed:tomcat-embed-jasper:8.5.4
+--- org.apache.tomcat.embed:tomcat-embed-core:8.5.4 -> 8.5.6
+--- org.apache.tomcat.embed:tomcat-embed-el:8.5.4 -> 8.5.6
\--- org.eclipse.jdt.core.compiler:ecj:4.5.1
后面dep.log 文件名可以随意,然而,你一定在想为什么有些带了(* )有的带了-> 有的什么都没有呢,这是什么鬼。前面已经说过,当发生版本冲突时Gradle 会采用最新版本解决。仔细观察带了(* )的依赖你会发现这些依赖被不同的库重复依赖了若干次,这里(* )的意思即是表示该依赖被忽略掉了。而-> 则表示其它的定级依赖的传递依赖中存在更高版本的依赖,该版本将会使用-> 后面的版本来替代。
1.3.11 反向查找
如果你想知道某个依赖到底被哪个库引用过,可以采用下面的命令进行反向查找
gradle dependencyInsight --dependency tomcat-embed-core > reverse.log
:dependencyInsight
org.apache.tomcat.embed:tomcat-embed-core:8.5.6 (conflict resolution)
+--- org.apache.tomcat.embed:tomcat-embed-websocket:8.5.6
| \--- org.springframework.boot:spring-boot-starter-tomcat:1.4.2.RELEASE
| \--- org.springframework.boot:spring-boot-starter-web:1.4.2.RELEASE
| \--- compile
\--- org.springframework.boot:spring-boot-starter-tomcat:1.4.2.RELEASE (*)
org.apache.tomcat.embed:tomcat-embed-core:8.5.4 -> 8.5.6
\--- org.apache.tomcat.embed:tomcat-embed-jasper:8.5.4
\--- compile
(*) - dependencies omitted (listed previously)
BUILD SUCCESSFUL
Total time: 6.936 secs
上面的报告中可以看到8.5.6这个版本后面标注了(conflict resolution ) 说明了该版本是用于解决冲突选用的版本。
1.3.12 冲突即停
Gradle 默认采用自动升级版本的方式解决依赖冲突,有时这种隐式升级可能会带来一些不必要的麻烦,此时我们可以通过更改这种默认行为来让Gradle 发现版本冲突时立即停止构建并抛出错误信息。 更改脚本:
configurations.all {
resolutionStrategy {
failOnVersionConflict()
}
}
执行gradle build 的输出结果:
:compileJava FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Could not resolve all dependencies for configuration ':compileClasspath'.
> A conflict was found between the following modules:
- org.apache.tomcat.embed:tomcat-embed-core:8.5.4
- org.apache.tomcat.embed:tomcat-embed-core:8.5.6
可以看到在执行gradle build 时由于tomcat-embed-core 存在版本冲突导致了构建失败,并提示了冲突的两个版本。
1.3.13 依赖报告
Gradle 官方提供了一个名叫project-report 插件可以让依赖查看更加简单方便,要用此插件只需要在脚本中添加apply plugin: 'project-report' 即可。该插件提供的任务可以参考下面的表格,所有输出结果将被放在build/report 下。
任务名称 | 描述 |
---|
dependencyReport | 将项目依赖情况输出到txt文件中 功能同gradle dependencies > build/dependenciestxt | htmlDependencyReport | 生成HTML版本的依赖情况 | propertyReport | 生成项目属性报告 | taskReport | 生成项目任务报告 | projectReport | 生成项目报告,包括前四个 |
1.3.14 使用插件检查更新
使用三方插件进行检查,可以使依赖固定在一个相对新的版本,这里需要注意的是,plugins 需要放置在脚本的顶部
plugins {
id "name.remal.check-dependency-updates" version "1.0.6"
}
应用此插件后,可以执行gradle checkDependencyUpdates 或 gradle cDU 检查依赖最新版本
Task :web:checkDependencyUpdates New dependency version: com.alibaba:druid: 1.0.29 -> 1.1.7
1.4 使用版本
1.4.1 强制使用版本
当然,有时候可能仅仅是需要强制使用某个统一的依赖版本,而不是排除他们,那么此时force 就该登场了。指定force = true 属性可以冲突时优先使用该版本进行解决。
compile('com.github.nanchen2251:CompressHelper:1.0.5'){
force = true
}
全局配置强制使用某个版本的依赖来解决依赖冲突中出现的依赖
configurations.all {
resolutionStrategy {
force 'com.github.nanchen2251:CompressHelper:1.0.5'
}
}
另一个例子
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support') {
if (requested.name.startsWith("support-")||
requested.name.startsWith("animated")||
requested.name.startsWith("cardview")||
requested.name.startsWith("design")||
requested.name.startsWith("gridlayout")||
requested.name.startsWith("recyclerview")||
requested.name.startsWith("transition")||
requested.name.startsWith("appcompat")) {
details.useVersion '25.0.0'
}
}
}
}
1.4.2 使用动态版本
如果你想让你的工程始终采用最新依赖 ,那么Gradle 提供了一种方式可以始终保证采用依赖的最新版本而无需每次手工检查修改版本。
使用加号+ ,可以让Gradle 在每次执行构建时检查远程仓库是否存在该依赖的新版本,如果存在新版本则下载选用最新版本。当然也可以指定依赖某个大版本下的最新子版本,1.+ 表示始终采用该依赖最新的1.x 版本的最新依赖。
compile 'com.android.support:support-v4:+'
compile 'com.android.support:support-v4:23+'
虽然这是看上去十分风骚的一种用法,但这无疑会降低你系统构建的速度同时提高构建失败的风险。因为Gradle 不得不每次检查远程仓库是否存在最新版本,同时新版本也可能带来无法预知的兼容性问题。
一个综合案例
compile('com.github.nanchen2251:CompressHelper:1.0.5') {
force = true
exclude module: 'CompressHelper'
exclude group: 'com.github.nanchen2251'
exclude group: 'com.github.nanchen2251', module: 'CompressHelper'
transitive = false
}
转载于:https://www.jianshu.com/p/7ccdca8199b8
2 下载安装
2.1 下载解压
官网下载地址: https://gradle.org/releases/ 下载解压的文件夹如下所示:
2.2 配置环境变量
配置环境变量,新建 GRADLE_HOME 环境变量指向 Gradle 解压路径的文件夹 %GRADLE_HOME%\bin 添加到 Path 环境变量中
2.3 配置Gradle仓库
gradle 和maven 类似,也需要配置仓库地址,首先新建一个文件夹,并在环境变量中配置默认的仓库地址GRADLE_USER_HOME
2.4 配置阿里云镜像
在gradle 中的init.d文件夹 中新建一个init.gradle文件 ,并在文件中添加如下配置
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/public/' }
mavenLocal()
mavenCentral()
}
}
2.5 验证
验证gradle 是否安装成功,打开cmd命令行输入 gradle -v
2.6 IDEA中配置Gradle
在IDEA 的Setting 里打开"Build, Execution, Deployment"-"Build Tools"-"Gradle" 勾选 Use local Gradle distribution ,在 Gradle home 中选择安装的Gradle 的路径。 如果在变量和配置文件中设置了Gradle 的仓库路径,在 Service directory path 中就会自动填写地址,如果想改的话可以手动修改 如图所示:
|