为什么我们需要Maven
編程項(xiàng)目構(gòu)建工具簡(jiǎn)介
在進(jìn)行編程操作的時(shí)候,我們常常會(huì)遇到很多與編程無關(guān)的項(xiàng)目管理工作,如下載依賴、編譯源碼、單元測(cè)試、項(xiàng)目部署等操作。一般的,小型項(xiàng)目我們可以手動(dòng)實(shí)現(xiàn)這些操作,然而大型項(xiàng)目這些工作則相對(duì)復(fù)雜。構(gòu)建工具是幫助我們實(shí)現(xiàn)一系列項(xiàng)目管理、測(cè)試和部署操作的工具。
軟件構(gòu)建(Software Build)是指軟件開發(fā)過程中涉及到的一系列處理工作,如將源代碼編譯成二進(jìn)制代碼,打包二進(jìn)制代碼,運(yùn)行自動(dòng)化測(cè)試等。為了方便編程人員的操作,人們開發(fā)了自動(dòng)構(gòu)建(Build Automation)工具來幫助人們處理這些工作。這篇博客將簡(jiǎn)單介紹構(gòu)建工具的概念。
- 一、為什么需要構(gòu)建工具
- 二、構(gòu)建工具的功能
- 三、流行的構(gòu)建工具
- 四、如何識(shí)別項(xiàng)目構(gòu)建工具
一、為什么需要構(gòu)建工具
在國內(nèi)高校的編程課中,比如,以合肥工業(yè)大學(xué)管理學(xué)院電子商務(wù)專業(yè)的編程課為例。一般都是從Java編程基礎(chǔ)教起,在安裝好JDK之后,開始使用一個(gè)記事本編寫一個(gè)HelloWorld.java,里面內(nèi)容是打印出Hello World。接下來教大家使用JCreator工具來編寫程序,實(shí)現(xiàn)輸出。之后的教學(xué)主要內(nèi)容在Java知識(shí)上,對(duì)于項(xiàng)目開發(fā)的經(jīng)驗(yàn)和知識(shí)則很少描述。于是很多同學(xué)不理解,為什么要用JCreator編寫程序,或者是JCreator很好用,為什么后面大家用Eclipse、IntelliJ IDEA這種工具來開發(fā)Java。
這些工具都是IDE,也就是為了方便開發(fā)人員組織代碼文件,管理項(xiàng)目而開發(fā)的。IDE其實(shí)也可以實(shí)現(xiàn)編譯、打包的功能。既然如此為何還需要構(gòu)建工具呢?為什么很多IDE軟件都提供了各種構(gòu)建工具插件呢?個(gè)人認(rèn)為,IDE本身是為了提供開發(fā)人員編程的工具,因此整合了項(xiàng)目管理和構(gòu)建的一些功能。而構(gòu)建工具的目標(biāo)是為了管理依賴、編譯、打包和部署。因此,后者更像是提供編程的依賴環(huán)境和外部包,并幫助發(fā)布部署項(xiàng)目的。它的對(duì)項(xiàng)目管理支持的功能和目標(biāo)比IDE更純粹也更強(qiáng)大(比如大部分構(gòu)建工具都有中央庫,收集了幾乎所有開源的外部包提供給開發(fā)者自動(dòng)導(dǎo)入外部包的功能,而一般IDE的這方面功能也是通過構(gòu)建工具實(shí)現(xiàn)的。同時(shí),構(gòu)建工具幾乎都支持命令行運(yùn)行,來幫助我們打包、編譯和發(fā)布等,證明它并不是提供一個(gè)編寫代碼的環(huán)境,而是作為管理的工具存在。而IDE幾乎都是有界面,可以提供代碼編寫的)。此外,網(wǎng)絡(luò)上還有一些說法(https://www.oschina.net/question/558461_117208):
一般而言.一個(gè)比較正規(guī)的項(xiàng)目都不會(huì)基于IDE 進(jìn)行構(gòu)建..一般會(huì)用ant, maven, gradle ,
為什么不用ide 呢?首先,是ide的選擇,有人喜歡,用vim,eclipse,intellijidea,收費(fèi)的,免費(fèi)的.
特別是公開的項(xiàng)目,你用什么IDE 相當(dāng)于為這個(gè)IDE 打廣告了..
所以,一般而言都是用構(gòu)建工具,而不是IDE .實(shí)際上各種IDE 也是基于各種構(gòu)建系統(tǒng),也正是不同的IDE,它們的構(gòu)建方式不同,所以要讓不同的IDE間能一起開發(fā),于是需要一個(gè)統(tǒng)一的構(gòu)建工具,只是你平時(shí)不關(guān)注而已..
對(duì)于小型的項(xiàng)目而言,比如大學(xué)開始的Java課可能要求我們寫一個(gè)簡(jiǎn)單的計(jì)算器等。該項(xiàng)目依賴的外部的代碼很少,幾乎使用Java自帶的SDK就可以了。但是,對(duì)于大中型的項(xiàng)目,都會(huì)依賴很多外部開發(fā)資源。網(wǎng)絡(luò)上開源了大量的代碼,在我們編寫程序的時(shí)候可以幫助我們減少重復(fù)性的工作,大大提升復(fù)用情況,降低編程難度。對(duì)于這種項(xiàng)目的代碼維護(hù),以及依賴維護(hù)是很復(fù)雜的。什么程序依賴什么版本的什么外部包,如果不使用構(gòu)建工具幫助我們管理這些依賴,那將增加開發(fā)人員大量的負(fù)擔(dān)。因此,包括上述編譯、打包和發(fā)布等功能,構(gòu)建工具在幫助我們管理這些東西,大大提升編程效率。
二、構(gòu)建工具的功能
基本上構(gòu)建的自動(dòng)化是編寫或使一大部分任務(wù)自動(dòng)執(zhí)行的一個(gè)動(dòng)作,而這些任務(wù)則是軟件開發(fā)者的日常,像是
三、流行的構(gòu)建工具
歷史上,自動(dòng)構(gòu)建工具主要是通過makefiles(環(huán)境變量文件)進(jìn)行,它是一個(gè)文件,包含了自動(dòng)構(gòu)建的指令。大多數(shù)情況下,makefile都是指示如何編譯并連接項(xiàng)目的。目前,不同的編程語言有不同的構(gòu)建工具。主要包括如下:
在Java的世界里,目前在被使用的常用構(gòu)建工具有三個(gè):Ant,Maven,Gradle。
Ant的核心是由Java編寫,采用XML作為構(gòu)建腳本,這樣就允許你在任何環(huán)境下,運(yùn)行構(gòu)建。Ant基于任務(wù)鏈思想,任務(wù)之間定義依賴,形成先后順序。缺點(diǎn)是使用XML定義構(gòu)建腳本,導(dǎo)致腳本臃腫,Ant自身沒有為項(xiàng)目構(gòu)建提供指導(dǎo),導(dǎo)致每個(gè)build腳本都不一樣,開發(fā)人員對(duì)于每個(gè)項(xiàng)目都需要去熟悉腳本內(nèi)容,沒有提供在Ant生態(tài)環(huán)境內(nèi)的依賴管理工具。
Maven團(tuán)隊(duì)意識(shí)到Ant的缺陷,采用標(biāo)準(zhǔn)的項(xiàng)目布局,和統(tǒng)一的生命周期,采用約定由于配置的思想,減少構(gòu)建腳本需要的編寫內(nèi)容,活躍的社區(qū),可以方便找到合適的插件,強(qiáng)大的依賴管理工具。缺點(diǎn)是采用默認(rèn)的結(jié)構(gòu)和生命周期,太過限制,編寫插件擴(kuò)展麻煩,XML作為構(gòu)建腳本。
而Gradle同時(shí)擁有Ant和Maven的優(yōu)點(diǎn),它是基于Groovy的DSL,提供聲明式的構(gòu)建語言,采用標(biāo)準(zhǔn)的項(xiàng)目布局,但擁有完全的可配置性,就是可以改,通過插件,提供默認(rèn)的構(gòu)建生命周期,也可以自己定義任務(wù),單獨(dú)運(yùn)行任務(wù),定義任務(wù)間的依賴,強(qiáng)大的依賴管理工具,與Maven和Ivy倉庫結(jié)合,與Ant天生兼容,有效的重用Ant的任務(wù),多種實(shí)現(xiàn)插件的方式,強(qiáng)大的官方插件庫,從構(gòu)建級(jí)別,支持從Ant或者M(jìn)aven的逐步遷移,通過包裝器,無縫的在各個(gè)平臺(tái)運(yùn)行。
四、如何識(shí)別項(xiàng)目構(gòu)建工具
一般地,一個(gè)項(xiàng)目的根目錄中就會(huì)包含構(gòu)建工具的配置文件信息,也表明了該項(xiàng)目使用的構(gòu)建工具,通常有如下的對(duì)應(yīng)關(guān)系:
接下來介紹一下Maven
背景
在使用Eclipse進(jìn)行項(xiàng)目合作開發(fā)的時(shí),我們是如何進(jìn)行項(xiàng)目依賴管理的呢?
我們通常會(huì)在新建項(xiàng)目的時(shí)候,同時(shí)建立一個(gè)lib目錄,在其中放著項(xiàng)目所依賴的各方類庫,這樣提交到SVN之后, 每個(gè)開發(fā)人員檢出項(xiàng)目到本地,得到項(xiàng)目的工作副本,這樣所有開發(fā)人員就會(huì)持有統(tǒng)一的項(xiàng)目依賴了。?
【1】依賴冗余,浪費(fèi)空間:但是,隨著項(xiàng)目的增多,模塊的增多,這種方式就會(huì)有問題。很多模塊都會(huì)引用相同的依賴,當(dāng)每個(gè)模塊都把自己的依賴提交到SVN,那么相同的依賴就會(huì)占用服務(wù)器SVN的Repository很大的空間,造成空間浪費(fèi)。?
【2】版本問題:同時(shí),如果一個(gè)項(xiàng)目中依賴的版本和另一個(gè)項(xiàng)目依賴的版本不一致。比如這個(gè)項(xiàng)目依賴hibernate2.x,而另一個(gè)可能依賴hibernate3.x, 當(dāng)合并兩個(gè)項(xiàng)目發(fā)布的時(shí)候, 可能因?yàn)檫@種依賴類庫詳細(xì)版本信息的缺失,造成問題。
是什么
為了解決以上依賴管理過程中出現(xiàn)的問題, 我們尋求出一種集中式的依賴管理方式。各個(gè)項(xiàng)目只要通過統(tǒng)一的依賴描述文件(pom.xml)來指定自己需要的依賴就可以, 而不用自己來管理真正的依賴庫,因?yàn)樗械捻?xiàng)目都使用同一個(gè)中央依賴庫(中央倉庫), 所以即使各個(gè)項(xiàng)目中有相同的依賴, 也不會(huì)出現(xiàn)依賴冗余的問題。?
Maven是基于POM的一款進(jìn)行項(xiàng)目依賴管理,構(gòu)建管理和項(xiàng)目信息管理的工具。
優(yōu)點(diǎn)
【1】“約定優(yōu)于配置(Convention over Configuration)”,maven提供了約定的項(xiàng)目的目錄結(jié)構(gòu),自動(dòng)創(chuàng)建項(xiàng)目目錄,提高開發(fā)效率。
目錄結(jié)構(gòu):?
main :源代碼?
test : 放測(cè)試類?
resource:資源文件
src?
–main?
—-java?
——package?
–test?
—-java?
——package
用命令自動(dòng)創(chuàng)建目錄的兩種方式:如:mvn archetype:generate
【2】簡(jiǎn)單的構(gòu)建過程:編譯、清理、測(cè)試、打包、部署等。通過輸入簡(jiǎn)單的命令就可以完成這些工作。?
compile:編譯?
test:測(cè)試?
package:打包?
clean?:清理target文件,刪除maven生成的目標(biāo)文件,target中存放的是編譯后的class文件和測(cè)試報(bào)告?
install:安裝jar包到本地倉庫中
Maven編譯項(xiàng)目流程
最原始的java程序運(yùn)行:
Java 程序是由若干個(gè) .class 文件組成的。這些 .class 文件必須根據(jù)它們所屬的包不同而分級(jí)分目錄存放;運(yùn)行前需要把所有用到的包的根目錄指定給 CLASSPATH 環(huán)境變量或者通過 java 命令的 -cp 參數(shù);運(yùn)行時(shí)還要到控制臺(tái)下去使用 java 命令來運(yùn)行。
使用maven編譯項(xiàng)目的流程:
1.執(zhí)行mvn compile 編譯源代碼?
2.如果用到其他jar包時(shí)會(huì)去pom.xml中的找坐標(biāo)?
3.根據(jù)坐標(biāo)去本地倉庫中查找:?
如果有,則將jar包加入到classpath中?
如果沒有,會(huì)去maven的中央倉庫找,下載放到本地倉庫中,再加入到classpath中。
基本概念
倉庫:管理和存放項(xiàng)目依賴?
坐標(biāo):項(xiàng)目依賴的唯一標(biāo)示
生命周期:?
clean :清理項(xiàng)目 clean?
default:構(gòu)建項(xiàng)目 compile test package install?
site:生成項(xiàng)目站點(diǎn)?
【備注】:如果執(zhí)行mvn package命令,會(huì)先自動(dòng)執(zhí)行compile 和test命令。
依賴范圍:?
有三種classpath(編譯,測(cè)試,運(yùn)行),規(guī)定依賴范圍就是控制依賴與classpath的關(guān)系, 不同的范圍,就會(huì)尋找不同classpath下的jar包進(jìn)行執(zhí)行。?
compile?【默認(rèn)方式】:編譯,測(cè)試,運(yùn)行,打包的時(shí)候都會(huì)加入依賴(默認(rèn)方式)?
provided:編譯和測(cè)試的時(shí)候會(huì)加入依賴,運(yùn)行時(shí)不會(huì),打包時(shí)不會(huì)。如:項(xiàng)目依賴Servlet API,但是運(yùn)行時(shí)不依賴。因?yàn)閃eb Container中已經(jīng)有,這樣避免了沖突 。?
runtime:在運(yùn)行和測(cè)試的時(shí)候依賴,在編譯的時(shí)候不依賴?
test:測(cè)試時(shí)依賴,編譯和打包時(shí)不依賴,測(cè)試時(shí)才有用的依賴。如:要把(如Junit)scope設(shè)置成test。?
system:和provided一樣?編譯和測(cè)試時(shí)候有效,與本機(jī)系統(tǒng)相關(guān)聯(lián),可移植性差?
import:導(dǎo)入的范圍,表示如將項(xiàng)目B中的依賴導(dǎo)入到A當(dāng)中,只是用在dependencyManagement中
依賴:
傳遞依賴:A–>B–>C,項(xiàng)目A對(duì)項(xiàng)目C是間接依賴,此時(shí)項(xiàng)目A中就會(huì)有項(xiàng)目C的jar包,可以通過exclution標(biāo)簽排除jar包。?
依賴沖突:?
1.短路優(yōu)先:A–>B–>C–>X(V1.0)同時(shí)A–>D–>X(V2.0),那么A會(huì)依賴V2.0的X。?
2.先聲明優(yōu)先:A–>B–>X(V1.0),A–>C–>X(V2.0),那么看在A的pom的xml文件中,先聲明的是B的依賴還是C的依賴,A會(huì)依賴先聲明者所依賴的該版本的X。?
聚合:需要將多個(gè)項(xiàng)目同時(shí)安裝時(shí),可以通過module進(jìn)行配置?
繼承:多個(gè)項(xiàng)目中含有共同的依賴,將這些依賴抽到父類中,用dependencyManagement標(biāo)簽進(jìn)行管理,子類中通過parent標(biāo)簽進(jìn)行繼承。
一個(gè)Maven編譯實(shí)例
?<span?style="font-size:?small;">[INFO]?Scanning?for?projects...??
2.?[INFO]?------------------------------------------------------------------------??
3.?[INFO]?Building?Maven?Hello?World?Project??
4.?[INFO]????task-segment:?[clean,?compile]??
5.?[INFO]?------------------------------------------------------------------------??
6.?[INFO]?[clean:clean?{execution:?default-clean}]??
7.?[INFO]?Deleting?directory?D:\code\hello-world\target??
8.?[INFO]?[resources:resources?{execution:?default-resources}]??
9.?[INFO]?skip?non?existing?resourceDirectory?D:?\code\hello-world\src\main\resources??
10.?[INFO]?[compiler:compile?{execution:?default-compile}]??
11.?[INFO]?Compiling?1?source?file?to?D:?\code\hello-world\target\classes??
12.?[INFO]?------------------------------------------------------------------------??
13.?[INFO]?BUILD?SUCCESSFUL??
14.?[INFO]?------------------------------------------------------------------------??
15.?[INFO]?Total?time:?1?second??
16.?[INFO]?Finished?at:?Fri?Oct?09?02:08:09?CST?2009??
17.?[INFO]?Final?Memory:?9M/16M??
18.?[INFO]?------------------------------------------------------------------------??
19.?</span>??
?
clean告訴Maven清理輸出目錄target/,compile告訴Maven編譯項(xiàng)目主代碼,從輸出中我們看到Maven首先執(zhí)行了clean:clean任務(wù),刪除target/目錄,默認(rèn)情況下Maven構(gòu)建的所有輸出都在target/目錄中;接著執(zhí)行resources:resources任務(wù)(未定義項(xiàng)目資源,暫且略過);最后執(zhí)行compiler:compile任務(wù),將項(xiàng)目主代碼編譯至target/classes目錄(編譯好的類為com/juvenxu/mvnbook/helloworld/HelloWorld.Class)。
上文提到的clean:clean、resources:resources,以及compiler:compile對(duì)應(yīng)了一些Maven插件及插件目標(biāo),比如clean:clean是clean插件的clean目標(biāo),compiler:compile是compiler插件的compile目標(biāo),后文會(huì)詳細(xì)講述Maven插件及其編寫方法。
至此,Maven在沒有任何額外的配置的情況下就執(zhí)行了項(xiàng)目的清理和編譯任務(wù),接下來,我們編寫一些單元測(cè)試代碼并讓Maven執(zhí)行自動(dòng)化測(cè)試。
1.?[INFO]?Scanning?for?projects...??
2.?[INFO]?------------------------------------------------------------------------??
3.?[INFO]?Building?Maven?Hello?World?Project??
4.?[INFO]????task-segment:?[clean,?test]??
5.?[INFO]?------------------------------------------------------------------------??
6.?[INFO]?[clean:clean?{execution:?default-clean}]??
7.?[INFO]?Deleting?directory?D:\git-juven\mvnbook\code\hello-world\target??
8.?[INFO]?[resources:resources?{execution:?default-resources}]??
9.?…??
10.?Downloading:?http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.pom??
11.?1K?downloaded??(junit-4.7.pom)??
12.?[INFO]?[compiler:compile?{execution:?default-compile}]??
13.?[INFO]?Compiling?1?source?file?to?D:?\code\hello-world\target\classes??
14.?[INFO]?[resources:testResources?{execution:?default-testResources}]??
15.?…??
16.?Downloading:?http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.jar??
17.?226K?downloaded??(junit-4.7.jar)??
18.?[INFO]?[compiler:testCompile?{execution:?default-testCompile}]??
19.?[INFO]?Compiling?1?source?file?to?D:\?code\hello-world\target\test-classes??
20.?[INFO]?------------------------------------------------------------------------??
21.?[ERROR]?BUILD?FAILURE??
22.?[INFO]?------------------------------------------------------------------------??
23.?[INFO]?Compilation?failure??
24.?D:\code\hello-world\src\test\java\com\juvenxu\mvnbook\helloworld\HelloWorldTest.java:[8,5]?-source?1.3?中不支持注釋??
25.?(請(qǐng)使用?-source?5?或更高版本以啟用注釋)??
26.?????@Test??
27.?[INFO]?------------------------------------------------------------------------??
28.?[INFO]?For?more?information,?run?Maven?with?the?-e?switch??
29.???… ?
不幸的是構(gòu)建失敗了,不過我們先耐心分析一下這段輸出(為了本書的簡(jiǎn)潔,一些不重要的信息我用省略號(hào)略去了)。命令行輸入的是mvn?clean?test,而Maven實(shí)際執(zhí)行的可不止這兩個(gè)任務(wù),還有clean:clean、resources:resources、compiler:compile、resources:testResources以及compiler:testCompile。暫時(shí)我們需要了解的是,在Maven執(zhí)行測(cè)試(test)之前,它會(huì)先自動(dòng)執(zhí)行項(xiàng)目主資源處理,主代碼編譯,測(cè)試資源處理,測(cè)試代碼編譯等工作,這是Maven生命周期的一個(gè)特性,本書后續(xù)章節(jié)會(huì)詳細(xì)解釋Maven的生命周期。
從輸出中我們還看到:Maven從中央倉庫下載了junit-4.7.pom和junit-4.7.jar這兩個(gè)文件到本地倉庫(~/.m2/repository)中,供所有Maven項(xiàng)目使用。
構(gòu)建在執(zhí)行compiler:testCompile任務(wù)的時(shí)候失敗了,Maven輸出提示我們需要使用-source?5或更高版本以啟動(dòng)注釋,也就是前面提到的JUnit?4的@Test注解。這是Maven初學(xué)者常常會(huì)遇到的一個(gè)問題。由于歷史原因,Maven的核心插件之一compiler插件默認(rèn)只支持編譯Java?1.3,因此我們需要配置該插件使其支持Java?5。
總結(jié)
所謂構(gòu)建就是包括編譯,運(yùn)行單元測(cè)試,生成測(cè)試報(bào)告(還記得target文件夾嗎?那里存放著測(cè)試報(bào)告),打包,部署等一系列過程。我們通過Maven可以方便地自動(dòng)化的完成這些,僅僅是通過一個(gè)mvn clean install命令。雖然我最初用它時(shí),以為它僅僅是一個(gè)管理項(xiàng)目依賴的工具,類似于NuGet(.NET平臺(tái)下的免費(fèi)、開源的包管理開發(fā)工具)。現(xiàn)在看來它的功能還有待我去實(shí)踐和發(fā)掘。有興趣地可以閱讀一下《Maven實(shí)戰(zhàn)》這本書。
總結(jié)
以上是生活随笔為你收集整理的为什么我们需要Maven的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 物联网无线连接服务发布阿里云全新产品,物
- 下一篇: bzoj5252 [2018多省省队联测