初識構建工具-gradle

構建工具的做用

  • 依賴管理javascript

  • 測試,打包,發佈html

主流的構建工具

  • Ant:提供編譯,測試,打包java

  • Maven:在Ant的基礎上提供了依賴管理和發佈的功能git

  • Gradle:在Maven的基礎上使用Groovy管理構建腳本,再也不使用XML來管理github

Gradle

一個開源的項目自動化構建工具,創建在Apache Ant 和Apache Maven概念的基礎上,並引入了基於Groovy的特定鄰域語言(DSL),而不在使用XML形式管理構建腳本。web

Gradle的下載與安裝

下載地址:https://gradle.org/releases編程

  • 配置環境變量,GRADLE_HOMEapi

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

  • 驗證是否安裝成功, gradle -vtomcat


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

相關文章
相關標籤/搜索