新一代的自动化构建工具 Gradle

项目自动化介绍

构建工具的作用

  • 依赖管理

    对各种 Jars 包和版本的管理

  • 测试、打包、发布

    对项目代码进行测试,测试完成后,将项目代码进行打包,发布服务器上

主流的构建工具有那些?

Gradle 介绍

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

Gradle 官网链接

​ 二十几岁正在为事业奋斗的男人就像搬砖工,

​ 搬起砖,就没手抱你,放下砖,就不能养你。

​ --多么悲情的浪漫,可你有没有想过为什么不能兼顾?

Gradle 快速入门

准备

  • 确保已经安装 JDK ,java -version
  • 从 Gradle 官网下载 Gradle,https://gradel.org

安装

  • 配置环境变量, GRADLE_HOME
  • 添加到 path, %GRADLE_HOME%\bin
  • 验证是否安装成功,gradle -v

Groovy 介绍

Groovy 是用于 Java 虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多的代码,同时又具有闭包动态语言中的其他特性

与 Java 相比

  • Groovy 完全兼容 Java 的语言
  • 分号是可选的
  • 类 、方法默认是 public 的
  • 编译器给属性自动添加 getter/setter 方法
  • 属性可以直接用点号获取
  • 最后一个表达式的值会被作为返回值
  • == 等同于 equals(), 不会有 NullPointerException

Groovy 的高级特性

  • assert 语句
  • 可选类型定义
  • 可选的括号
  • 字符串
  • 集合 API
  • 闭包

基于 Gradle 创建 Java 项目

Gradle 下载,下载速度比官网快

打开 IDEA,创建新项目

配置 Groovy

若不选择本地 Groovy ,IDEA 会自动从网络上下载 Groovy


打开 Groovy Console 测试

测试代码

public class ProjectVersion{

    // 定义一个大版本号
    private int major
    // 定义一个小版本号
    private int minor

    // 提供有参构造方法
    public ProjectVersion(int major, int minor){
        this.major = major
        this.minor = minor
    }

    // 提供 setter、getter 方法
    public void setMajor(int major){
        this.major = major;
    }
    public int getMajor(){
        return this.major;
    }

}

ProjectVersion v1 = new ProjectVersion(1, 0)
println v1.major
println v1.minor   // Groovy 提供了 setter 、 getter 方法

ProjectVersion v2 = null
println v1 == v2  // 验证 == 等同于 equals 方法

Groovy 高效特性

  1. 可选类型定义(使用 def 关键字声明变量,类型自动推断)
  2. 可选的括号
  3. 字符串(字符串,双字符串,三字符串)
  4. 集合 API
  5. 闭包(将函数作为参数传递)
// 可选类型定义
def version = 1
def g = 'gradle'
// assert 语句
//assert 1==1
// assert version == 1

// 可选的括号
//println version.getClass()
//println version

// 字符串
def s1 = 'Hello Gradle'
def s2 = "Hello ${g}"
def s3 = '''Hello
          Gradle'''
//println s1
//println s2
//println s3

// 集合 API
def buildTools = ['ant', 'maven']
//buildTools.add('gradle')
buildTools << 'gradle'
//println buildTools
//println buildTools.getClass()
//println buildTools.size() == 3

def buildYears = ['ant':2000, 'maven':2004]
buildYears.gradle = 2008
//println buildYears.ant
//println buildYears['gradle']
//println buildYears.getClass()

// 闭包
def c1 = {
    param ->
        println "Hello ${param}"
}

def c2 = {
    println "Hello c1"
}

def method1(Closure closure){  // 注意: Closure 不要导包
    closure('c1')
}

def method2(Closure closure){
    closure()
}

method1 c1  // 相当于 method1(c1)
method2 c2  // 相当于  method2(c2)

第一个 Gradle 项目

TODO 应用程序,只实现添加待办事项

  • TodoItem.java

    package com.szxy;
    
    /**
     * @Author:zwer
     * @Date:2020/2/13 20:58
     * @Description:com.szxy.com.szxy
     * @Version:1.0
     **/
    public class TodoItem {
        private String name; // 事项名称
        private boolean hasDone; // 是否已经完成
    
        public TodoItem(String name, boolean hasDone){
            this.name = name;
            this.hasDone = hasDone;
        }
    
        public TodoItem(String name){
             this(name, false);
        }
    
        public TodoItem(){}
    
        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: "+name+" hasDone:"+(hasDone ? "has done it" : "need to do it");
        }
    }
    
    
  • App

    package com.szxy;
    
    import java.util.Scanner;
    
    /**
     * @Author:zwer
     * @Date:2020/2/13 20:56
     * @Description:com.szxy
     * @Version:1.0
     **/
    public class App {
        public static void main(String[] args) {
            Scanner input = new Scanner(System.in);
            int i = 0;
            while(++i > 0){
                System.out.println(i+" .Please input your todo:");
                TodoItem todoItem = new TodoItem(input.nextLine());
                System.out.println(todoItem);
            }
        }
    }
    
  • 使用 Gradle 进行项目构建

    点击 jar 按钮后,若构建成功,则会在 build/libs 目录生成 jar 包文件

  • 在 IDAE 终端下运行 jar

  java -classpath build/libs/20202013-todo-1.0-SNAPSHOT.jar com.szxy.App

第一个 Gradle 的 Web 项目

Gradle 6.1 参考 war plugin

  • 修改 build.gradle 文件

    plugins {
        // id 'java'  
        id 'war'  // 官网  
    }
    
    group 'com.szxy'
    version '1.0-SNAPSHOT'
    
    sourceCompatibility = 1.8
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        testCompile group: 'junit', name: 'junit', version: '4.12'
    }
    
    
  • 项目结构

  • index.html

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Todo</title>
        <script>
            function addTodo(){
                let todo = document.getElementsByTagName("input")[0]
                let your_todo = document.getElementById("your_todo").getElementsByTagName("ul")[0]
                your_todo.innerHTML += "<li>"+ todo.value +"</li>"
            }
            function clearTodo(){
                document.getElementsByTagName("input")[0].value = ""
            }
        </script>
    </head>
    <body>
        <div id="container"  style="width:500px">
            <h1>待办事项</h1>
            <hr/>
            <label>请输入你所需要的待办事项:</label>
            <input type="text" name="todo" />
            <button onclick="addTodo()" >添加</button>
            &nbsp;&nbsp;
            <button onclick="clearTodo()">清空</button>
            <hr/>
            <h2>你确定的待办事项</h2>
            <div id="your_todo">
                <ul></ul>
            </div>
        </div>
    </body>
    </html>
    
  • 构建项目

  • 点击 war 按钮,在 build/libs 目录下生成 war 包

  • war 包丢到 tomcat 目录下的 webapps 中,然后启动 tomcat,访问 http://localhost:8888/20202013-todo-1.0-SNAPSHOT/

Gradle 基本原理

构建脚本

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

项目-任务之间的关系

项目(Project):一个项目代表一个正在构建的组件(比如一个 jar 文件),当构建启动后, Gradle 会基于 build.gradle 实例化一个 org.gradle.api.Project 类,并且能够通过 project 变量使其隐式可用。

  • group 、name 、version
  • apply 、dependencies、repositories、 task
  • 属性的其他配置方式: ext、gradle.properties

Task (task): 任务对应 org.gradle.api.Task 。主要包括任务动作和任务依赖。任务动作定义了一个最小的工作单元。可以定义依赖于其他任务、动作序列和执行条件

  • dependsOn
  • doFirst 、doLast <<

自定义任务

自动创建目录结构

def createNewDir = {
    path ->
        File file = new File(path)
        if(!file.exists()){
            if(path.endsWith(".xml")){
               file.createNewFile()
            }
            file.mkdirs()
        }
}

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

task createWebAppDir {
    dependsOn 'createJavaDir'   // createWebAppDir 依赖于 createJavaDir
    def paths = ['src/main/webapp', 'src/main/webapp/WEB-INF','src/main/webapp/WEB-INF/web.xml']
    doLast {
        paths.forEach(createNewDir)
    }
}

构建生命周期

钩子方法

依赖管理

概述:几乎所有的基于 JVM 的软件都需要依赖外部类库来重用现有的功能。自动化的依赖管理可以明确依赖的版本,可以解决因传递性依赖带来的版本冲突

工件坐标: group name version

常用仓库: mavenLocal mavenCentral jcenter 自定义 maven 仓库 文件仓库

repositories { // 仓库使用顺序是从上往下
    maven {
        url ''  // 私服
    }
    mavenLocal()  // 本地仓库
    mavenCentral() // 中央仓库
}

依赖传递:若 A->B, B->C, 则 A->C。若 A 依赖于 B,B 依赖于 C,则 A -> C

依赖阶段配置:编译代码:compile、runtime 测试代码:testCompile、testRuntime

大多依赖都是编译时依赖即 compile,只有少数情况下使用运行时依赖 runtime,比如 JDBC 的驱动属于运行时依赖。一般情况下使用别人的依赖时,依赖文档中会说明那种依赖的,若实在分不清依赖是编译时依赖还是运行时依赖,选择编译时依赖也没有问题。

解决版本冲突

当导入新的依赖于原有的依赖发生版本冲突(jar 包的版本不一致,导致版本冲突),Gradle 会自动处理版本冲突的 jar 包,若 Gradle 处理不了,则采用默认解决策略-出现版本冲突,则直接构建失败。

这里解决版本冲突在 Gradle 4.3 以下的版本中使用。

  • 修改默认解决策略

    configurations.all {
    	resolutionStrategy{
    		failOnVersionConflict() // 出现版本冲突,则直接构建失败
    	}
    }
    
  • 排除传递性依赖

    compile('org.hibernate:hibernate-core:3.6.3.Final'){
    	exclude group:"org.slf4j", module:"slf4j-api" //transitive = false
    }
    
  • 强制指定一个版本

    configurations.all{
    	resolutionStrategy{
    		force 'org.slf4j:slf4j-api:1.7.24'
    	}
    }
    

Gradle 深入实战

多项目构建

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

TODO 依赖关系:

model 模块下 build.gradle 文件中 dependencies 配置:

dependencies {
    compile project(':model')  // repository 依赖于 model
}

web 模块下 build.gradle 文件中 dependencies 配置:

dependencies {
    compile project(':repository')
}

根目录下 setting.gradle 文件:

rootProject.name = 'todo'
include 'model'
include 'repository'
include 'web'

配置子项目

配置要求:

  • 所有项目应用 Java 插件

  • Web 子项目打包成 WAR

  • 所有项目添加 logback 日志功能

  • 统一配置公共属性

项目范围:

多项目构建实战

根目录下 build.gradle 文件配置

allprojects {  // 为所有模块配置 Java 插件
    apply plugin:  'java'
    sourceCompatibility = 1.8
}

subprojects {
    repositories { // 仓库使用顺序是从上往下
        mavenCentral() // 中央仓库
    }
    dependencies { // 所有项目添加 logback 日志功能
        implementation 'com.hynnet:logback-classic:1.1.3'
        testCompile group: 'junit', name: 'junit', version: '4.12'
    }
}

repositories { // 仓库使用顺序是从上往下
    maven {
        url ''  // 私服
    }
    mavenLocal()  // 本地仓库
    mavenCentral() // 中央仓库
}

在根项目目录下新建 gradle.properties 文件,统一配置公共属性

group='com.szxy.todo'
version='2.0-SNAPSHOT'

自动化测试

Gradle 官网关于使用 Java 项目的测试

概述:

一些开源的测试框架比如 Junit,TestNG 能够帮助我们编写可复用的结构化的测试,为了运行这些,你要先编译它们,就像编译源代码一样。测试代码的作用仅仅用于测试的情况,不应该被发布到生产环境中,需要把源代码和测试代码分开来。

项目布局:

测试配置(添加依赖):

dependencies {
    testImplementation 'junit:junit:4.12'
}

test {
    useJUnit()

    maxHeapSize = '1G'
}

测试发现:

  • 任何继承自 junit.framework.TestCasegroovy.util.GroovyTestCase 的类
  • 任何被 @RunWith 注解的类
  • 任何至少包含一个被 @Test 注解的类
public class TodoRepositoryImplTest {

    private TodoRepository repo = new TodoRepositoryImpl();

    @Test
    public void saveTest(){
        TodoItem item = new TodoItem("eat food");
        repo.save(item);
        //Assert.assertNull(item.getName());
         Assert.assertNotNull(item.getName());
    }

}

发布

发布到本地或者远程:

配置 maven 插件

Gradle 官网中关于 maven插件配置

apply plugin: 'maven-publish'

publishing {
    publications {
        myPublish(MavenPublication){
            groupId = 'com.szxy.todo'
            version = '1.0'
            from components.java
        }
    }
    repositories {
        maven {
            name "myRepo"
            url ""
        }
    }
}

Gradle 总结

慕课学习视频

Gradle 课程,老师讲的不错
这是 Gradle 课程链接地址,欢迎大家来一起学习,共同进步!

Gradle 补充

  1. 设置阿里 Gradle 国内镜像源
  2. 使用 Gradle 构建 SpringBoot 2.x 应用程序
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值