依赖管理javascript
测试,打包,发布html
Ant:提供编译,测试,打包java
Maven:在Ant的基础上提供了依赖管理和发布的功能git
Gradle:在Maven的基础上使用Groovy管理构建脚本,再也不使用XML来管理github
一个开源的项目自动化构建工具,创建在Apache Ant 和Apache Maven概念的基础上,并引入了基于Groovy的特定邻域语言(DSL),而不在使用XML形式管理构建脚本。web
下载地址:https://gradle.org/releases编程
配置环境变量,GRADLE_HOMEapi
添加到path,%GRADLE_HOME%\bin;数组
验证是否安装成功, gradle -vtomcat
Groovy是用于Java虚拟机的一种敏捷的动态语言,他是一种成熟的面向对象的编程语言,既能够用于面向对象编程,也能够用做纯粹的脚本语言。使用该种语言没必要编写过多的代码,同时又具备闭包和动态语言的其余特性。
与Java相比较,Groovy彻底兼容Java语法,分号是可选的,类和方法默认都是public,编译器给属性自动添加getter/setter方法,属性能够直接用点号获取,再也不须要经过方法来获取,同时最后一个表达式的值会被做为返回值,==等同于equals(),不会有NullPointerException。
Groovy的高效特性中,自带了assert语句,它是弱类型的编程语言,直接经过def来定义类型;同时它的括号是可选的,字符串有多种写法。
IDE:idea
打开Groovy的控制台,Tools -> Groovy Console
def version = 1
def不是一个明确的类型,它相似于javascript里面的var,定义一个变量,变量的类型是自动推断出来的,在这里version就是int类型。
assert version == 2
这是一个失败的断言,由于version是等于1,执行后返回false
println version
执行结果任然是1
def s1 = 'zzh'//单纯的字符串 def s2 = "gradle version is ${version}"//能够插入变量 def s3 = '''zzh is handsome''' println s1 println s2 println s3
s1表示单纯字符串,s2能够插入变量,s3则是能够换行
buildTools的默认类型是ArrayList,向里面追加一个元素buildTools << 'gradle'。
def buildTools = ['ant','maven'] buildTools << 'gradle' println buildTools.getClass() assert buildTools.getClass() == ArrayList assert buildTools.size() == 3
buildYears的默认类型为LinkedHashMap,在Map上添加一个元素直接用点号就行,元素的访问有两种类型,能够用点号也能够用字符串
def buildYears = ['ant':2000,'maven':2004] buildYears.gradle = 2009 println buildYears.ant println buildYears['gradle'] println buildYears.getClass()
简单来讲就是一个代码块,跟方法同样能够有参数也能够没有,还能够被赋值给一个变量,也能够看成一个参数传递给一个方法。
//包含参数的闭包,参数v省略了类型,“->”后面是方法体 def c1 = { v -> println v } //不包含参数的闭包 def c2 = { println 'hello' } //使用闭包做为参数,Closure是groovy自带的,不要导入java里面的包 def method1(Closure closure){ closure('param') //带参数的闭包 } def method2(Closure closure){ closure() //不带参数的闭包 } def method3(Closure closure) { closure() } method1(c1) method2(c2) method3 { println '省略方法参数括号直接传入闭包' }
先进行实例演示,以后再讲解具体步骤功能。
Gradle管理jar包,经过在控制台输入代办事项的名称在控制台显示代办事项。
先手动建立src相关目录:
package com.zzh.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; } @Override public String toString() { return name + (hasDone ? " hasDone" : " need to do") + "!"; } }
package com.zzh.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); } } }
jar:把当前的源文件编译完后打包成jar包。
build:根据项目中的build.gradle构建脚本,脚本中的语句apply plugin: 'java'即便用java构建插件,因此构建完后也是jar包的形式。
clean:清楚以前的构建。
点击jar后,及进行编译java,处理资源文件,生成字节码,打包成jar包
由于这个jar包里面有包含main方法的类,因此能够直接执行,而没有main方法的jar包通常会做为依赖被其余的工程引用。
在终端执行
Gradle管理Web应用程序的功能
添加webapp目录:
其中log.properties只是用于演示打包以后这个配置文件会在压缩包什么位置。
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>
加上apply plugin: 'war' 以后刷新,看到build下面多出了war构建功能,点击以后,找到war包:
将war包放在本机Tomcat的webapp文件夹下,启动tomcat:
war包自动解压缩后的文件夹中,能够看到配置文件和字节码是同级目录,路径是正确的。
构建块:
Gradle中的两个基本概念是项目(Project)和任务(task),每一个构建至少包含一个项目,项目中包含一个或多个任务。在多项目构建中,一个项目能够依赖于其余项目;相似的,任务能够造成一个依赖关系图来确保他们的执行顺序。
项目1依赖于项目2,任务A依赖于任务B和C,因此B和C要在A以前执行完。
项目:
一个项目表明一个正在构建的组件(好比一个jar文件),当构建启动后,Gradle会基于build.gradle实例化一个org.gradle.api.Project类,而且可以经过project变量使其隐式可用。
项目的主要属性:
项目的主要方法:
apply:应用一个插件
dependencies:声明项目依赖于哪些jar或者是其余项目
repositories:指定仓库,根据group,name,version惟一肯定一个组件
task: 声明项目里的任务
任务(task)
任务对应org.gradle.api.Task,主要包括任务动做和任务依赖。任务动做定义了一个最小的工做单元。能够定义依赖于其余任务,动做序列和执行条件。
任务的主要方法:
dependsOn:声明任务依赖
doFirst:在任务列表的最前面添加一个动做。
doLast:在任务列表的末尾添加一个动做。
在前面建立目录结构的时候都是手动建立,如今用自定义任务来完成自动建立目录结构的功能。
传进的参数是字符串路径:
def createDir = { path -> File dir = new File(path) if (!dir.exists()) { dir.mkdirs() } }
定义一个数组,里面存放目录路径,同时给任务添加一个动做doFirst
task makeJavaDir() { def paths = ['src/main/java', 'src/main/resources', 'src/test/java', 'src/test/resources'] doFirst{ paths.forEach(createDir) } }
执行makeJavaDir后目录被建立:
由于java工程有的目录Web工程都有,只是Web工程多了webapp目录,可使用web工程依赖java工程,这样web只用建立特有的目录便可.添加doLast动做:
task makeWebDir(){ dependsOn 'makeJavaDir' def paths = ['src/main/webapp','src/test/webapp'] doLast { paths.forEach(createDir) } }
先将test文件夹删除,测试是否会先执行makeJavaDir:
先执行makeJavaDir,在执行makeWebDir,目录也被建立好了:
Gradle会根据构建脚本创造一个Project类,并在构建脚本中隐式可用。
生成task的依赖顺序和执行顺序,并初始化任务。好比:
task loadVersion{ project.version='1.0' }
执行动做代码。例如doLast:
task loadVersion <<{ print 'success' }
几乎全部的基于JVM的软件项目都须要依赖外部类库来重用现有的功能。自动化的依赖管理能够明确依赖的版本,能够解决因传递性依赖带来的版本冲突。
group,name,version三个属性能够惟一肯定一个jar包。
仓库用来存放jar包
编译时依赖的jar包在运行时都会依赖,在运行时依赖的在编译阶段不会依赖。源代码依赖的测试代码都会依赖,反之则不必定。
好比在build.gradle里的
dependencies { compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.1' testCompile group: 'junit', name: 'junit', version: '4.12' }
testCompile就是测试编译阶段依赖的jar包,compile则是编译阶段依赖的jar包logback。
在App.java里面添加日志:
repositories能够配置多个仓库,除了默认的中央仓库mavenCertral,还能够配置mavenLocal()本地仓库,当有多个仓库的时候是按仓库的顺序去查找jar包。
还能够配置私服仓库,地址就写在url里面。
repositories { mavenLocal() mavenCentral() maven{ url '' } }
查看依赖报告
排除传递性依赖
强制一个版本
Gradle会自动依赖最高版本的jar包,这是gradle的默认处理方式。
好比如今添加hibernate-core的依赖:
dependencies { compile group: 'org.hibernate',name: 'hibernate-core', version: '3.6.3.Final' compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.1' testCompile group: 'junit', name: 'junit', version: '4.12' }
hibernate-core对slf4j-api有依赖,依赖的版本是1.6.11,而以前的logback-classic对slf4-api也有依赖,版本是1.7.22,如今加入以后被强制依赖最高的版本1.7.22:
执行 Tasks -> help -> dependencies 任务看到详细的依赖转变:
当出现版本冲突的时候,不使用最新的包,直接让它构建失败,这样就能知道哪些出现了版本冲突。
configurations.all{ resolutionStrategy{ failOnVersionConflict() } }
加入以后报错:
configurations.all{ resolutionStrategy{ force 'org.slf4j:slf4j-api:1.7.22' } }
在企业项目中,包层次和类关系比较复杂,把代码拆分红模块一般是最佳实践,这须要清晰的划分功能边界,好比把业务逻辑和数据持久化划分开来。项目符合高内聚低耦合时,模块化就变得很容易,这是一条很是好的软件开发实践。
建立模块repository,model,web;经过右键todo项目 -> New -> Module;
将原来的src拖进web子模块中。
打开settings.gradle生成目录结构:能够看到根项目是todo,剩下三个项目都是子模块要include进来的。
如今让repository模块依赖model模块,web模块依赖repository模块,这样web模块也能依赖model模块了。
dependencies { compile project(":model") testCompile group: 'junit', name: 'junit', version: '4.12' }
dependencies { compile project(":repository") compile group: 'org.hibernate',name: 'hibernate-core', version: '3.6.3.Final' testCompile group: 'junit', name: 'junit', version: '4.12' }
查看依赖关系能够看到web模块依赖于repository模块,repository模块有依赖于modle模块:
先把原来的自定义任务删除
全部项目应用Java插件
web子项目打包成WAR
全部项目添加logback日志功能
统一配置公共属性
如今存在一个问题,项目和子项目中都有apply plugin: 'java',能够进行统一处理,将子项目中的apply plugin: 'java'和sourceCompatibility = 1.8都删除,在根项目的build.gradle下经过allprojects进行配置:
group 'com.zzh.gradle' version '1.0-SNAPSHOT' apply plugin: 'war' allprojects{ apply plugin: 'java' sourceCompatibility = 1.8 } repositories { mavenLocal() mavenCentral() } dependencies { compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.1' testCompile group: 'junit', name: 'junit', version: '4.12' }
将根项目下的apply plugin: 'war' 删除,在web子项目的build.gradle中加上就行。
使用subprojects来演示配置logback的功能,用allprojects也能达到一样效果。将根项目中的repositories和dependencies放入subprojects中,这样子项目中依赖就不用配置junit
root下的build.gradle:
group 'com.zzh.gradle' version '1.0-SNAPSHOT' allprojects{ apply plugin: 'java' sourceCompatibility = 1.8 } subprojects{ repositories { mavenLocal() mavenCentral() } dependencies { compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.1' testCompile group: 'junit', name: 'junit', version: '4.12' } } repositories { mavenLocal() mavenCentral() }
当多项目构建的时候,每一个子项目的build.gradle只是对其余子项目的依赖或者其余的个性化配置,共同的都会放在根项目下进行配置。
一些开源的测试框架好比JUnit,TestNG能够编写可复用的结构化的测试,为了运行这些测试,须要先编译。测试代码的做用仅仅用于测试的状况,不该该被发布到生产环境中,须要把源代码和测试代码分开来。
package com.zzh.gradle.todo.repository; import com.zzh.gradle.todo.model.TodoItem; import java.util.HashMap; import java.util.Map; public class TodoRepository { private static Map<String, TodoItem> items = new HashMap<>(); public void save(TodoItem item) { System.out.println("" + item); items.put(item.getName(), item); } public TodoItem query(String name) { return items.get(name); } }
package com.zzh.gradle.todo.repository; import com.zzh.gradle.todo.model.TodoItem; import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.*; public class TodoRepositoryTest { private TodoRepository repository = new TodoRepository(); @Test public void save() throws Exception { TodoItem item = new TodoItem("zzh"); repository.save(item); Assert.assertNotNull(repository.query(item.getName())); } }
执行repository子模块的build任务,查看结果:
根项目的build.gradle中使用插件“maven-publish”
group 'com.zzh.gradle' version '1.0-SNAPSHOT' allprojects{ apply plugin: 'java' sourceCompatibility = 1.8 apply plugin: 'maven-publish' publishing{ publications{ myPublish(MavenPublication){ from components.java } } } repositories{ maven{ name "myRepo" url "" } } } subprojects{ repositories { mavenCentral() } dependencies { compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.1' testCompile group: 'junit', name: 'junit', version: '4.12' } }
myPublish是本身定义的方法名字,component.java表示把java产生的输出发布到仓库里面。repositories里的仓库名字能够本身定义,url地址通常来讲就是私服地址。应用插件以后gradle的tasks增长了publishing:
执行全部的MavenLocal任务:publishToMavenLocal
打开本地仓库(根据本身配置的地址):
使用maven-publish插件
配置要发布的内容和仓库地址
执行发布任务