依賴管理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插件
配置要發佈的內容和倉庫地址
執行發佈任務