新一代构建工具gradle
一.项目自动化介绍
1.石器时代:自动化构建工具产生之前
依赖管理:将所有使用到的jar包放到lib目录下面,多的上百个,很容易产生版本冲突
测试:能不写就不写,写了也是一个一个写一下main方法
打包:Eclipse的导出jar包、war包
上传:通过FTP上传到服务器解压运行
2.构建工具的作用
a.依赖管理,包括各种jar包、各种版本
b.能自动执行测试、打包、发布
c.机器能干的活,绝不自已动手
3.主流构建工具
Ant:编译、测试、打包
Maven:依赖管理、发布
Gradle:Groovy
4.Gradle是什么
一个开源的项目自动化构建工具,建立在Apache Ant和Apache Maven概念的基础上,并引入了基于Groovy的特定领域语言(DSL),而不再使用XML形式管理构建脚本。
二.准备使用Gradle
1.Gradle安装
确保已经安装JDK,java -version
从Gradle官网下载Gradle,https://gradle.org/
配置环境变量:GRADLE_HOME
添加到path,%GRADLE_HOME%\bin;
验证是否安装成功,gradle -v


2.gradle下载下来解压后

bin目录下就两个可执行文件,一个是Windows下的可执行文件,一个是Linux下的可执行文件
init.d目录下有一个readme.txt文件,所有初始化脚本都在这个目录下,也就是说每次执行之前都会把这个目录下的脚本执行一遍
lib目录下就是gradle本身所依赖的jar包,因为它是基于JVM的,所有它所依赖的都是jar包形式
media目录下都是一个图标文件,没有什么特别的
3.groovy基础知识-理论介绍
Groovy是用Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的其他特性。
与Java比较:
a.Groovy完全兼容Java的语法
b.分号是可选的
c.类、方法默认是public的
d.编译器给属性自动添加getter/setter方法
e.属性可以直接用点号获取
f.最后一个表达式的值会被作为返回值
g.==等同于equals(),不会有NullPointerExceptions
高效的Groovy特性
a.assert语句
b.可选类型定义
c.可选的括号
d.字符串
e.集合API
f.闭包
4.groovy基础知识-与java比较
操作实例




这里的JDK也选择我本地安装的JDK

然后我们打开groovy的窗口。

然后用Java的方式写一个Bean,如下所示:

然后用groovy语法改造这个Bean如下,依然正确。

简单运用运行结果如下:

5.groovy基础知识-高效特性

在groovy里面,assert在任何地方都是可以执行的。

如下定义一个list,然后通过<<方式追加一个元素,assert判断是类型和大小



6.groovy基础知识-重点

三.第一个Gradle项目
1.介绍
ToDo应用程序,只实现添加待办事项
Java应用程序版
Web版
2.TODO-应用程序版本


自动生成的build.gradle内容如下:

目录结构

编写简单示例代码:

App.java
package com.bijian.gradle.todo; import java.util.Scanner; public class App { public static void main(String[] args) { int i = 0; Scanner scanner = new Scanner(System.in); while(++i > 0) { System.out.println(i + ".please input todo item name"); TodoItem item = new TodoItem(scanner.nextLine()); System.out.println(item); } } }
TodoItem.java
package com.bijian.gradle.todo; public class TodoItem { //待办事项名称 private String name; //已完成 private boolean hasDone; public TodoItem(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isHasDone() { return hasDone; } public void setHasDone(boolean hasDone) { this.hasDone = hasDone; } public String toString() { return name + (hasDone ? " hasDone":"need to do") + "!"; } }
jar:把当前源文件编译完成后打包成jar包
build:也可以实现打成jar包的同样功能,因为build就是执行build.gradle这个构建脚本,这里使用的是java插件,所以构建完成后也是jar包的形式
classes:将源代码编译成class类
clean:清除之前的构建
点击这个jar执行后,即会把当前源文件编译完成并打成jar包。执行时如出现如下错误:

通过如下方式进行设置:
File ->Settings ->Editor ->File Encodings 这种方式修改的文件编码方式只对当前 project 起作用,每次新建了一个工程后还需要重新设置编码方式。
File ->Other Settings ->Default Settings ->Editor ->File Encodings ,这儿设置的是默认的文件编码方式,所有新建的工程使用的都是默认的文件编码方式。

设置完成后,重新双击jar执行正常,在build->libs下新生成jar文件。

执行这个jar包(因为这个jar包中有一个main方法),命令:java -classpath build/libs/todo-1.0-SNAPSHOT.jar com.bijian.gradle.todo.App

3.TODO-WEB版
新增webapp目录及其下文件夹和文件,并在resources下新增log.properties(这里只是看最终打包后把这个文件放到war包的哪个地方)
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="3.1" metadata-complete="true"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> </web-app>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>todo</title> <script type="text/javascript"> function addTodo() { var name = document.getElementById("name").value; var list = document.getElementById("list"); list.innerHTML = list.innerHTML + "<p>"+name+"</p>"; } </script> </head> <body> <h1>TODO-web版</h1> <label>请输入待办事项名称:</label><input id="name" type="text"/> <button onclick="addTodo()">添加</button> <h3>待办事项列表</h3> <div id="list"></div> </body> </html>
然后在build.gradle中增加:apply plugin: 'war',将会在右边多出一个war,同样双击运行成功后将在build/libs下产生war包。

将打出来的war包拷贝到本地安装的tomcat的webapps目录下,然后双击bin目录下的startup.bat启动tomcat服务器,启动成功后,在浏览器中输入http://localhost:8080/todo-1.0-SNAPSHOT/index.html运行效果如下:

这里我们再到tomcat的webapps目录下,可以看到todo-1.0-SNAPSHOT.war被自动解压。

打开todo-1.0-SNAPSHOT目录


四.高级应用
1.构建脚本介绍
构建块:Gradle构建中的两个基本概念是项目(project)和任务(task),每个构建至少包含一个项目,项目中包含一个或多个任务。在多项目构建中,一个项目可以依赖于其他项目;类似的,任务可以形成一个依赖关系图来确保他们的执行顺序。

项目(Project):一个项目代表一个正在构建的组件(比如一个jar文件),当构建启动后,Gradle会基于build.gradle实例化一个org.gradle.api.Project类,并且能够通过project变量使其隐式可用。常用的属性:group、name、version,这三个属性就是一个组件的坐标。重要的方法是apply、dependencies、repositories、task。
apply:应用一个插件,它是在Project上的一个方法。
dependencies:用来声明依赖于哪些jar包或其它项目。
repositories:是一个仓库,它就是说去哪个仓库去找依赖的jar包,也就是说先找到仓库,在仓库里再根据group、version唯一确定一个组件。
task:用来声明项目里有什么任务的。
属性的其他配置方式:ext(可以直接在这上面定义属性,可以直接引用)、gradle.properties(在gradle.properties里面可用键值对的方式来声明属性)
任务(task):任务对应org.gradle.api.Task。主要包括任务动作和任务依赖。任务动作定义了一个最小的工作单元。可以定义依赖于其他任务、动作序列和执行条件。有dependsOn、doFirst、doLast<<重要方法。dependsOn是声明任务依赖的。task也是一个动作列表,doFirst在任务列表的最前面添加一个动作,doLast在动作的末尾添加一个动作,一个任务里面可以执行多次doFirst、doLast,也就是说动作列表里面可以包含多个动作。
隐式可用,如group 'com.bijian.gradle'其实就是project.group='com.bijian.gradle',按住Ctrl鼠标点击,可以打开Project类,可以看到,它们其实就是调用Project的setGroup方法。

同理,version调用的是setVersion方法。名称是在settings.gradle里的,有一个rootProject.name='todo'。settings.gradle是用来管理多项目构建的。
apply是PluginAware接口的一个方法,由于Project继承了这个接口,所以它也有apply方法。


repositories、dependencies也是调用Project里的repositories、dependencies方法,

我们可以自已定义任务,但是我们使用插件以后,默认是带了很多任务的,如下所示:


2.自定义任务
自定义一个任务来帮我们自动创建目录结构的功能。
def createDir = { path -> File dir = new File(path); if(!dir.exists()) { dir.mkdirs(); } } task makeJavaDir() { def paths = ['src/main/java','src/main/resources','src/test/java','src/test/resources'] doFirst { paths.forEach(createDir) } } task makeWebDir() { dependsOn 'makeJavaDir' def paths = ['src/main/webapp','src/test/webapp'] doLast { paths.forEach(createDir) } }
makeJavaDir和makeWebDir都是创建目录,其中makeWebDir依赖makeJavaDir。另外,doLast和doFirst在这个应用中没有区别,用哪个都行。

自定义任务在other下,双击运行可以看到效果。以后我们每次新建工程之后,就可以把这个代码放到build.gradle里面,如果觉得这样很麻烦的话,可以把这个功能做成插件。
3.构建生命周期
初始化:gradle会根据构建脚本创建一个项目,也就是一个Project类,并且在这个构建脚本中隐式可用。在多项目构建中,这个阶段也是很重要的,它会初始化所有将要参与到构建中的项目。
配置:用来生成task的依赖顺序以及执行顺序,根据配置代码来生成的,配置代码就是除了动作代码外的就是配置代码。
执行:执行动作代码,执行完后一个构建就完成了。

上面自定义的任务里面,doFirst和doLast中的代码就是动作代码,是执行阶段会执行的代码,其它属于配置阶段的代码,dependsOn只能在配置阶段执行,因为配置阶段就要决定任务的依赖关系和执行顺序。
gradle是非常灵活的,还为我们提供了勾子方法。

一句话,初始化阶段就是初始化项目,有哪些项目需要参与到构建当中。配置阶段就是生成task的依赖关系和执行图。执行阶段就是执行task的动作。
4.依赖管理
几乎所有的基于JVM的软件项目都需要依赖外部类库来重用现有的功能。自动化的依赖管理可以明确依赖的版本,可以解决因传递性依赖带来的版本冲突。
工作坐标:group、name、version。通过这三个属性可以唯一确定一个jar包。
仓库就是用来存放jar包的地方,常用仓库:
a.mavenLocal:通过Maven使用过的jar包都会在本地仓库存有一份,mavenCentral/jcenter:公网上的公共仓库
b.在实际工作中最常用的是自定义maven仓库,也就是私服。
c.文件仓库:就是本地机器上的文件路径,也可以做为仓库。这个非常不建议大家去使用,因为我们使用构建工具就是为了让构建一致性,到处构建,结果应该都是一样的,如果跟具体的机器有关的话,就违反了我们使用构建工具的初忠,当然,这个实在有特殊需求的话可以使用一下。
依赖的传递性
B依赖A,如果C依赖B,那么C依赖A

依赖阶段配置
源代码两个阶段:compile、runtime
测试代码也有两个阶段:testCompile、testRuntime

运行时阶段是扩展于编译阶段的,如果在编译阶段依赖的jar包在运行时都会依赖,如果在运行时依赖的编译阶段不会依赖。如果是源代码依赖的,测试代码都会依赖,测试代码依赖的,源代码不一定依赖。

‘将testCompile改testRuntime,将变成运行阶段依赖。如果修改后一定要刷新的话,可以点击工程右键,勾选上Auto-import。

mavenCentral的网址是:https://search.maven.org/
如果我们要给工程添加一个日志功能,要添加日志的依赖,如使用logback。

添加到我们的工程中后,工程会自动下载依赖jar包。

添加运行后发现我们成功的添加了logback的依赖。我们没有自已去写logback的实现,没有写代码,就是重用了别人的轮子。即使用别人的依赖,实现自已的需求。

大多数情况下都是使用compile时依赖,因为我们编译时需要使用,运行时也需要使用。有一些情况,如jdbc的实现类,作为一个驱动,编译源代码因为使用的是接口,可以不用实现,因为JDK里在包含了jdbc的接口,驱动里面包含具体实现类,我们编译时只需有接口就可以了,而运行的时候就需要具体的实现类,所以这个时候就可以使用运行时依赖。当然,如果使用编译时依赖也是可以的。实在区分不了的话,都可以使用编译阶段的依赖。
依赖的写法

ch.qos.logback:logback-classic:1.2.1,这种是冒号分隔的简短的写法,第一个冒号之前的是groupId,第二个冒号之前的是name,最后是version。group、name、version就是坐标,在仓库中就可以通过这个坐标唯一确定一个jar包。
我们也可以配置多个仓库,如果有多个仓库,就是按仓库的顺序查找jar包的,如果前面的仓库查找到jar包了,就不会到后面的仓库中去查找。
mavenLoacl()就是本地的maven仓库。

传递性依赖

5.解决版本冲突

解决版本冲突的具体步骤:
a.查看依赖报告
b.排除传递性依赖
c.强制一个版本。
gradle的默认处理版本冲突的方法是:会自动帮我们解决版本冲突,会自动依赖最高版本的jar包。
修改默认解决策略
configurations.all{
resolutionStrategy{
failOnVersionConflict()
}
}
这样,当出现版本冲突的时候,不使用最新的包,直接让他构建失败,这样我们就知道哪些出现了版本冲突,要不然我们很难发现哪些出现了版本冲突。
解决冲突:
a.排除传递性依赖
compile('org.hibernate:hibernate-core:3.6.3.Final') {
exclude group:"org.slf4j",module:"slf4j-api"
//transitive=false
}
exclude group:"org.slf4j",module:"slf4j-api":排除具体jar包的依赖,这里的module就是我们坐标中的name属性,版本是不需要指定的,这是一种把低版本的依赖排除掉。
transitive=false:排除所有的传递性依赖
b.强制指定一个版本
我们可以指定为最新的版本,或者比当前所有的依赖都要高的版本
configurations.all{ resolutionStrategy{ force 'org.slf4j:slf4j-api:1.7.24' } }
操作实例:

此时,我增加hibernate依赖

此时,我们点击Tasks->help->dependencies


这样我们可以清晰地看到详细的依赖,这里构建成功了,说明gradle默认帮我们处理了版本冲突。
我们修改策略,如果发现版本冲突,就构建失败。

这时,我们可以使用排除传递性依赖方法解决如下:

此时可以构建成功。
也可以使用另外一种方法,就是强制指定一个版本。

当然,也可以在仓库中查到最新的版本,强制指定到最新的版本。
6.多项目构建介绍
项目模块化:在企业项目中,包层次和类关系比较复杂,把代码拆分成模块通常是最佳实践,这需要你清晰的划分功能的边界,比如把业务逻辑和数据持久化拆分开来。项目符合高内聚低耦合时,模块化就变得很容易,这是一条非常好的软件开发实践。

配置要求:
所有项目应用Java插件
web子项目打包成WAR
所有项目添加logback日志功能
统一配置公共属性

7.多项目构建实战
a.子模块创建



点“完成”,web模块就建好了。

然后将web模块下的src目录全部删除,将外面工程的src拖入。

成功后,我们看到settings.gradle内容如下:

用同样的方法创建model、repository两个模块。

需说明的是repository模块依赖model模块,需增加如下模块的依赖关系。

此时,settings.gradle内容也同步发生了变化。

在web模块中新增对repository模块的依赖,然后能看到传递性依赖model模块。

b.依赖关系的处理
model中的build.gradle中的内容可以不用配置了,根模块配置就可以了

repository模块的build.gradle内容如下:
repositories { mavenCentral() } dependencies { compile project(":model") }
web模块的build.gradle内容如下:
apply plugin: 'war' repositories { mavenCentral() } dependencies { compile project(":repository") }
根路径下的build.gradle内容如下:
allprojects{ apply plugin: 'java' sourceCompatibility = 1.8 } subprojects { repositories { mavenCentral() } dependencies { compile 'ch.qos.logback:logback-classic:1.2.1' testRuntime group: 'junit', name: 'junit', version: '4.12' } } repositories { mavenCentral() }
并在根路径下新建gradle.properties,内容如下:这里是统一配置group和version
group=com.bijian.gradle
version=1.0-SNAPSHOT
说明:

右边的构建根项目下的命令会依次执行各个子项目对应的命令,如果想单独执行某个子项目的命令,单独执行某个子项目下的命令就可以了。
如执行根模块下的clean。

如执行web模块的war命令,会对先执行依赖模块的任务,如下所示:

小结:settings.gradle用来管理项目和子项目的,每个子项目下的build.gradle只用来配置当前子项目个性化的需求,所有相同的配置都要在根项目的build.gradle进行配置,当然,也可以在子配置中进行配置,但这样会出现重复配置,我们后期不太好维护。
8.自动化测试
一些开源的测试框架比如JUnit、TestNG能够帮助你编写可复用的结构化的测试,为了运行这些测试,你要先编译它们,就像编译源代码一样。测试代码的作用仅仅用于测试的情况,不应该被发布到生产环境中,需要把源代码和测试代码分开来。

测试配置:
dependencies{ testCompile 'junit:junit:4.11' }

如果test执行失败,后面的步骤就不会执行了,构建就会失败,并且给提示具体的错误信息。

我们在repository模块下新增测试代码。

双击运行repository下的build命令,如下所示:

此时,打开repository->build->reports->tests->test下的index.html,查看测试运行结果。


修改测试断言,测试失败,可以看到如下的具体信息。

9.发布

一般最终发布到我们公司的私服上去,供其它项目使用或其他同事使用,我们最常用的仓库就是maven仓库,因为gradle没有自已的仓库,ivy现在开发很少使用了。
发现用maven-publish这个插件,在这个插件下在配置两个属性就可以了,如下所示:

配置后可以在右边看到多出publishing目录及下面的任务。

执行完publishMyPublishPublicationToMavenLocal命令后,将会在本地Maven仓库中产生相应的jar包。



简单来说,我们用三步就可以发布到仓库里面去,第一步使用maven publish这个插件,第二步配置我们要发布什么、仓库地址,第三步执行一下发布任务就可以了。
五.课程总结

posted on 2019-04-14 13:50 bijian1013 阅读(310) 评论(0) 收藏 举报
浙公网安备 33010602011771号