构建工具的作用

  • 依赖管理

  • 测试,打包,发布

主流的构建工具

  • Ant:提供编译,测试,打包

  • Maven:在Ant的基础上提供了依赖管理和发布的功能

  • Gradle:在Maven的基础上使用Groovy管理构建脚本,不再使用XML来管理

Gradle

一个开源的项目自动化构建工具,建立在Apache Ant 和Apache Maven概念的基础上,并引入了基于Groovy的特定邻域语言(DSL),而不在使用XML形式管理构建脚本。

Gradle的下载与安装

下载地址:https://gradle.org/releases

  • 配置环境变量,GRADLE_HOME

  • 添加到path,%GRADLE_HOME%\bin;

  • 验证是否安装成功, gradle -v


Groovy

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

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则是可以换行

集合api -> List

buildTools的默认类型是ArrayList,向里面追加一个元素buildTools << 'gradle'。

def buildTools = ['ant','maven']
buildTools << 'gradle'
println buildTools.getClass()
assert buildTools.getClass() == ArrayList
assert buildTools.size() == 3

集合api -> Map

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 '省略方法参数括号直接传入闭包'
}


快速实现添加待办事项应用程序

先进行实例演示,之后再讲解具体步骤功能。

Java应用程序版

Gradle管理jar包,通过在控制台输入代办事项的名称在控制台显示代办事项。

创建todo应用程序

先手动创建src相关目录:

代办事项的bean:TodoItem.java

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") + "!";
}
}

从控制台读取代办事项的名称并打印:App.java

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);
}
}
}

打开右侧Gradle projects

jar:把当前的源文件编译完后打包成jar包。

build:根据项目中的build.gradle构建脚本,脚本中的语句apply plugin: 'java'即使用java构建插件,所以构建完后也是jar包的形式。

clean:清楚之前的构建。

点击jar后,及进行编译java,处理资源文件,生成字节码,打包成jar包

执行jar包

因为这个jar包里面有包含main方法的类,所以可以直接执行,而没有main方法的jar包一般会作为依赖被其他的工程引用。

在终端执行

Web版

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>

build.gradle添加war插件

加上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变量使其隐式可用。

项目的主要属性:

  • group:组
  • name:名称,等同于maven里的ArtifactId
  • version:版本号

项目的主要方法:

  • 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()
}
}

在build.gradle中声明任务并给任务添加动作

定义一个数组,里面存放目录路径,同时给任务添加一个动作doFirst

task makeJavaDir() {
def paths = ['src/main/java', 'src/main/resources', 'src/test/java', 'src/test/resources']
doFirst{
paths.forEach(createDir)
}
}

执行自定义任务

执行makeJavaDir后目录被创建:

定义任务创建Web的目录

因为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'
}
}

多项目构建

项目模块化

在企业项目中,包层次和类关系比较复杂,把代码拆分成模块通常是最佳实践,这需要清晰的划分功能边界,比如把业务逻辑和数据持久化划分开来。项目符合高内聚低耦合时,模块化就变得很容易,这是一条非常好的软件开发实践。

TODO模块依赖关系

配置子项目

创建模块repository,model,web;通过右键todo项目 -> New -> Module;

将原来的src拖进web子模块中。

打开settings.gradle生成目录结构:可以看到根项目是todo,剩下三个项目都是子模块要include进来的。

现在让repository模块依赖model模块,web模块依赖repository模块,这样web模块也能依赖model模块了。

  • repository的build.gradle:
dependencies {
compile project(":model")
testCompile group: 'junit', name: 'junit', version: '4.12'
}
  • web的build.gradle:
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日志功能

  • 统一配置公共属性

所有项目应用Java插件

现在存在一个问题,项目和子项目中都有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'
}

web子项目打包成WAR

将根项目下的apply plugin: 'war' 删除,在web子项目的build.gradle中加上就行。

所有项目添加logback日志功能

使用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只是对其他子项目的依赖或者其他的个性化配置,共同的都会放在根项目下进行配置。


Gradle测试

自动化测试

一些开源的测试框架比如JUnit,TestNG可以编写可复用的结构化的测试,为了运行这些测试,需要先编译。测试代码的作用仅仅用于测试的情况,不应该被发布到生产环境中,需要把源代码和测试代码分开来。

TodoRepository.java

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插件

  • 配置要发布的内容和仓库地址

  • 执行发布任务


总体流程

GitHub地址:

learning_gradle

初识构建工具-gradle的更多相关文章

  1. 构建工具Gradle

    1.Summary   从Android团队开始宣布放弃Eclipse转投Android Studio时,构建工具Gradle进入了Android开发者的视野.而随着热修复.插件化.编译时注解的流行, ...

  2. 构建工具Gradle安装和简单使用

    1. 安装 到gradle官网下载页 https://gradle.org/gradle-download/ 下载gradle,其中“完全版(Complete distribution)”包含除了运行 ...

  3. 新一代构建工具gradle学习

    简介:Gradle的出现,是技术发展的必然,站在了Ant.maven等构建工具的肩膀上,使用了一种强大且具有表达性的基于Groovy的领域特定语言(DSL),使其拥有易用且灵活的方式去实现定制逻辑.方 ...

  4. 构建工具-----Gradle(二)-----myeclipse 10和myeclipse2015安装gradle插件----其他版本的myeclipse类似

    我们需要给myeclipse安装gradle的插件.这样myeclipse就能识别到gradle项目了,直接加载进去即可. 我们先安装配置系统命令行的gradle,挺简单的,下载后配置环境变量即可,详 ...

  5. 项目构建工具Gradle的使用入门(参考,只表明地址)

    Gradle入门介绍:简介 http://blog.jobbole.com/71999/ Gradle入门介绍:第一个Java项目 http://blog.jobbole.com/72558/ Gra ...

  6. 基于 Groovy 的自动化构建工具 Gradle 入门(转)

    本人工作之初没有使用自动化构建,后来敏捷了,开始使用 Ant - 完全面向过程的定义步骤,不进行依赖管理.再发展到 Maven,面向对象的方式管理工程,有了依赖的管理,JAR 包统一从中央仓库获得,保 ...

  7. java之项目构建工具Gradle

    介绍 Java 作为一门世界级主流编程语言,有一款高效易用的项目管理工具是 java 开发者共同追求的心愿和目标.显示 2000 年的 Ant,后有 2004 年的 Maven 两个工具的诞生,都在 ...

  8. 自动化构建工具gradle安装教程(使用sdkman安装)

    gradle是什么?(wiki解释) Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具.它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的 ...

  9. 项目构建工具gradle

    1.安装 https://gradle.org/install 2.构建一个项目 https://guides.gradle.org/creating-new-gradle-builds/ 3.bui ...

随机推荐

  1. java重载(实现同一方法名,不同参数)

    背景:  前几天写连接数据库时,因为要执行sql,有的是指向得到所有的执行结果,有的是想根据执行结果获得某一个字段的结果.这时我想通过同一个方法名,不同的参数,获得不同的结果.结果发现java的方法竟 ...

  2. 早上突然看明白 shader和材质球的关系

    计算机的世界不外乎 指令+数据 shader即Gpu指令,材质即数据

  3. Distributing Ballot Boxes

    Distributing Ballot Boxes http://acm.hdu.edu.cn/showproblem.php?pid=4190 Time Limit: 20000/10000 MS ...

  4. 整合Spring框架和Hibernate框架

    -------------------siwuxie095                                 整合 Spring 框架和 Hibernate 框架         1.导 ...

  5. UVa 11988 Broken Keyboard (a.k.a. Beiju Text)(链表)

    You're typing a long text with a broken keyboard. Well it's not so badly broken. The only problem wi ...

  6. sql批量修改字段内容的语句-SQL技巧

    --update '表名' set 要修改字段名 = replace (要修改字段名,'被替换的特定字符','替换成的字符')--update tRecord set columnName = rep ...

  7. struts框架值栈问题二之值栈的内部结构

    2. 问题二 : 值栈的内部结构 ? * 值栈由两部分组成 > root -- Struts把动作和相关对象压入 ObjectStack 中--List > context -- Stru ...

  8. Castle ActiveRecord学习(二)配置、引用、程序启动

    来源:http://www.cnblogs.com/zxj159/p/4082987.html 配置数据库驱动: Model层引用:Castle.ActiveRecord.dll.NHibernate ...

  9. CentOS7下搭建基本LNMP环境,部署WordPress

    系统环境:CentOS Linux release 7.4.1708 (Core) 3.10.0-693.el7.x86_64 软件版本:nginx-1.12.2.tar.gz php 7.1.11 ...

  10. C语言字符串操作函数实现

    1.字符串反转 – strRev void strRev(char *str) { assert(NULL != str);   int length=strlen(str); ; while(end ...