网上的教程,乃至官方文档在建立项目介绍上是选择跳过的,因此一开始,我查看了很多教程都是模模糊糊的,教程中都是在build.gradle中写上applu plugin ‘java‘ 然后就一下子出现了一个完整的java项目结构(后面仔细介绍build.gradle),摸索思考了下,人类历史最伟大的进步便是学会使用工具,而我们手上正有IDE这伟大的工具,而IDE正正让我跳过了官方文档的第一个坑。我们团队应该大多都是使用IDEA进行开发的,因此我下面的Demo都是利用idea的。
3.此时应该发现Gradle的建立和Maven相差无几,把上面的两项勾上,让IDE去做它该做的事情,点击下一步。
4.继续点击下一步。
5.此时我们发现一个熟悉的java项目结构出现了
6.点击打开build.gradle,下面大致说明一下此文件的意义
在Gradle中,有两个基本概念:项目和任务。请看以下详解:
项目是指我们的构建产物(比如Jar包)或实施产物(将应用程序部署到生产环境)。一个项目包含一个或多个任务。
任务是指不可分的最小工作单元,执行构建工作(比如编译项目或执行测试)。
build.gradle是gradle项目的构建脚本,里面指定了项目与任务。紧接着仔细说一下IDEA构建出项目后build.gradle默认的语句含义。
group ‘JryLearnGradle‘,version ‘1.0-SNAPSHOT‘ :
build名,版本号。
apply plugin: ‘java‘ :
Gradle与Maven一样,特性都是由plugin提供的,Gradle为我们提供了许多有用的插件,apply plugin :‘java‘ ,则是把gradle为java开发写的插件源码引用到build.gradle中(gradle中的plugin都是用groovy写好的代码)
repositories {
mavenCentral()
} :
Gradle仓库可以使用maven或ivy的仓库,我们团队基本使用maven,而IDEA默认使用了Central Maven 2的仓库,而加入maven仓库,gradle提供了三种选择给我们使用,分别是:
mavenCentral()别名,表示依赖是从Central Maven 2 仓库中获取的。
jcenter()别名,表示依赖是从Bintary’s JCenter Maven 仓库中获取的。
mavenLocal()别名,表示依赖是从本地的Maven仓库中获取的。
我们可以更改代码 mavenCentral() 选择不用的方式,当然gradle还提供了另外一种方式让我们构建依赖仓库来源,那便是url的方式,下面是使用url方式的方法:
repositories {
maven {
http://maven.aliyun.com/nexus/content/groups/public url ""
}
}
dependencies {
compile group: ‘foo‘, name: ‘foo‘, version: ‘0.1‘
testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.11‘
} :
这处申明外部依赖,这些依赖存放在外部仓库中。一个外部依赖可以由以下属性指定:
group属性指定依赖的分组(在Maven中,就是groupId)。
name属性指定依赖的名称(在Maven中,就是artifactId)。
version属性指定外部依赖的版本(在Maven中,就是version)。
testCompile是test需要的外部依赖,而compile则是项目运行需要的依赖声明,依赖的声明可以使用快捷方式:
dependencies {
compile ‘foo:foo:0.1‘
testCompile ‘junit:junit:4.11‘
}
默认的build.gradle文件也就解释完了,后面再一点点补充,现在回到我们跑demo的目的,我们是为了建立一个多项目的Spring boot,因此我们需要为SpringBootInGradle添加module,操作和maven差不多。
额外打开一下setting.gradle文件,发现里面就一句 rootProject.name = ‘SpringBootInGradle‘ ,就是project,没啥其他东西,先不管,往后操作。
7.右键项目名,new module
8.分别建立JryCore,JryBasicServer子module,点击next
9.此时我们的项目结构如下图
此时发现IDE实在太好用了,什么都自动为我们做好了,但作为一个学习者,还是得知道IDE在这期间究竟为我们做了什么,先来看看module中的build.gradle吧
group ‘JryLearnGradle‘
version ‘1.0-SNAPSHOT‘
apply plugin: ‘java‘
sourceCompatibility = 1.5
repositories {
mavenCentral()
}
dependencies {
testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.11‘
}
和一开始的没什么区别,其他几个builg.gradle也是如此,再来看看被我们一直忽略的settings.gradle,此时发现settings.gradle多了两行代码:
rootProject.name = ‘SpringBootInGradle‘
include ‘JryCore‘
include ‘JryBasicServer‘
而include的两个module正是我们刚刚添加的,由此推倒,我们需要为一个project增加module只需要在project中的setting.gradle写入include ‘xxxx’ 。
很简练,就是和我们推倒的一样,接着干。
此时发现,project中有三个build.gradle,里面的东西都一模一样,重复,如论从哪点考虑,我们作为一个多project的project,应该有module通用的依赖。接下来我们要做的是做到module在parent中的build.gradle配置。
9.把module中重复的配置转移到根项目的构建脚本,build.gradle修改添加代码,如下图:
此时发现两段内容代码都是一样的,还是重复,再修改,如下图:
第一个方式是针对某个project的操作,第二个是对根项目下的子项目的通用配置方式
如果是多项目构建中的所有项目共享,可以在根项目中的build.gradle加入allproject{}
10.在JryBasicServer中建立一个类HelloWorld,类中定义方法getString()
11.修改根项目build.gradle文件,如下图
build.gradle中除compile project(‘:JryBasicServer‘),其他都是前面解释过的语句,而compile projec的意思便是在JryCore中引入JryBasicServer项目,在打包时,gradle会先把JryBasicServer加载完后再到JryCore。
build.gradle修完完后,点击refresh all gradle projects,gradle将下载依赖,此时应该发现gradle从
https://repo1.maven.org/maven2中拉取依赖,这肯定不是我们想要的,国内墙太厚,gradle能不能像maven一样,引用阿里的私库呢?
能。
(1)我们先新建一个init.gradle文件,里面内容为:
allprojects{
repositories {
def REPOSITORY_URL = ‘http://maven.aliyun.com/nexus/content/groups/public‘
all { ArtifactRepository repo ->
def url = repo.url.toString()
if ((repo instanceof MavenArtifactRepository) && (url.startsWith(‘https://repo1.maven.org/maven2‘) || url.startsWith(‘https://jcenter.bintray.com‘))) {
project.logger.lifecycle ‘Repository ${repo.url} replaced by $REPOSITORY_URL .‘
remove repo
}
}
maven {
url REPOSITORY_URL
}
}
}
(2)把init.gradle文件放置在C:\Users\USER_NAME\.gradle中,此时gradle下载依赖时将会先执行init.gradle文件,此时发现下载依赖速度总算正常。
这里提一下init.gradle的作用和加载顺序
先来简单介绍一下init.gradle这个文件的作用。
- 它可以用来建立公司内部的配置,如定义公司内部的仓库地址。
- 它可以用来配置一些全局属性,比如配置持续集成服务器的地址等配置。
- 它可以用来提供构建所需要的用户的个人信息,如仓库或数据库的用户名和密码。
- 它可以用来定义开发者机器的环境,比如定义jdk安装在什么位置,android sdk安装在什么位置等等。
- 最重要的功能之一,它可以用来注册一些监听器。比如监听Gradle事件的发生,做一些额外的操作,例如需要对某个项目构建前和构建后做一些操作,又例如对项目的依赖做检测,检测是否含有snapshot包,在release构建中一般来说是禁止依赖snapshot包的,所以这时候就可以扔出一个异常。
- 重定向日志。我们可以将gradle默认的日志进行重定向,甚至我们可以不输出默认日志,自定义如何输出gradle产生的日志信息。
init.gradle的加载顺序:
1.加载USER_HOME/.gradle/init.gradle文件
2.加载USER_HOME/.gradle/init.d/目录下的以.gradle结尾的文件
3.加载GRADLE_HOME/init.d/目录下的以.gradle结尾的文件
11.依赖齐全后,在JryCode新建类PrintHelloController,内容如下
12.接下来就是run啦,成功跑起后,访问localhost:8080
此时一个多project的SpringBoot版HelloWorld就完成了,需要打包只需点击Gradle projects的build
成功build后,我们可以发现在libs中有我们需要的jar包了。
一个简单的demo就跑完了,以上都是走马观花式的操作,下面来分析一下Gradle的原理与总结吧。
深入理解Gradle背后的内容
在理解Gradle内容前,我们需要先理解一下何谓DSL。
DSL,领域特定语言,其基本思想是“求专不求全”,不像通用目的语言那样目标范围涵盖一切软件问题,而是专门针对某一特定问题的计算机语言。而Martin Fowler将DSL分为两类:外部DSL和内部DSL。外部DSL是一种独立的可解析的语言,举一个最常见的是例子,SQL,它专注于数据库的操作。内部DSL是通用语言所暴露的用来执行特定任务的API,它利用语言本身的特性,将API以特殊的形式(或者格式)暴露出来的。
总结一下,外部DSL是一种特定的独立语言,内部DSL是通用语言为实现特殊目的提供的API。
Gradle开发团队为了使用Gradle更好的构建描述,为Gradle提供了一套内部DSL,语言正是基于上文提及到的Groovy,理解了内部DSL,就能知道我们在跑demo时用到的各种dependenices{},project{},等等语句,底层其实就是调用用groovy代码封装好的api方法,继而也能理解Gradle中的plugin其实也就是用groovy写好的代码块,apply一个plugin,其实也相当于我们的开发的import。
了解了Gradle底层的实现,就应该来学习Gradle的设计思想。
Gradle是按照DDD(领域驱动设计)的思想设计和开发的,其三个领域对象:Project、Task、Action。而官方文档是这么说的,一个构建是由多个project组成,一个project是由多个task组成,task之间的依赖关系,构成了整个构建的流水线。下面仔细说一下三个领域对象的意义。
Project:
Project是Gradle最重要的一个领域对象,我们写的build.gradle脚本的全部作用,其实就是配置一个Project实例。
Task:Gradle的
Task等同于Ant的Target。 我们跑demo时在build.gradle写的dependenices{},project{},等等就是一个个的task,task是闭包的。
Action:Task可以包含n个
Action,Task提供了doFirst和doLast方法来给自己添加Action,如下面的例子一样。
.task myTask {
doFirst {
println ‘hello‘
}
doLast {
println ‘world‘
}
}
稍微总结一下上文,通过我们跑的demo,结合刚刚介绍,Gradle没有一开始的神秘,我们其实可以把它理解为api,它的配置文件其实就是我们一直编写的代码,我们要使用某功能的时候,按往常一样,去看一下官方API就行了
明白了Gradle底层设计和设计思想,接下来补充一下Gradle运行的生命周期。
Gradle的生命周期
1. Initialization -初始化阶段,初始化阶段会执行项目根目录下的settings.gradle文件,来分析哪些项目参与构建。
2. Configuration -配置阶段,配置阶段会去加载所有参与构建的项目的build.gradle文件,会将每个build.gradle文件实例化为一个Gradle的project对象。然后分析project之间的依赖关系,下载依赖文件,分析project下的task之间的依赖关系。
3. Execution -执行阶段,执行阶段来执行具体的task。
总结Gradle
Gradle对比Maven,最直观的便是,Maven的POM用XML写,内容标准,以致有所古板,而Gradle的配置文件则使用groovy编写,用代码编写的Gradle更加灵活,同时也更加简洁,而其提供的plugin也为其提供强大的功能。
Gradle对比Ant,Ant同样使用XML,XML文件臃肿庞大,Gradle的DSL就显得更加灵活。
前面两面两点都是说Gradle的优势,约定人性化,灵活性更高,但这里有必要提一下,就效率而言,Gradle与Maven和Ant并相差无几。毕竟没有完美的东西。
以上都是我个人在学习Gradle的笔记,内容不全,不能顾全全部,gradle还有很多很多没接触到的地方没提及到,后面再有心得,一点一点补充。