Maven學習第4期---Maven簡單使用
一、Maven常用命令
在學習使用Maven構建專案之前,先來了解一下Maven一些實用的命令。mvn的命令有很多,在專案開發中,理解了下面的幾個常用命令後,運用maven就基本沒有問題了。Maven常用的命令如下:
1.1 常用的mvn命令
mvn archetype:create 建立 Maven 專案mvn compile 編譯主程式原始碼,不會編譯test目錄的原始碼。第一次執行時,會下載相關的依賴包,可能會比較費時mvn test-compile 編譯測試程式碼,compile之後會生成target資料夾,主程式編譯在classes下面,測試程式放在test-classes下mvn test 執行
1.2 一些高階功能命令
跳過測試類 : -Dmaven.test.skip=true
下載jar包原始碼: -DdownloadSource=true
下載javadocs: -DdownloadJavadocs=true
例如:
mvn package -Dmaven.test.skip=true :表示打包時忽略掉test目錄,不編譯。 mvn install -DdownloadSource=true :本地安裝jar包時,下載jar的原始檔包。
二、Maven簡單使用
到目前為止,我們已經大概瞭解並安裝好了Maven。為了進一步學習Maven,我們和其他專案同樣以一個最簡單的HelloWorld專案開始,探究Maven的核心概念。通過對該專案的學習,來讓我們對構建生命週期 (Build Life Cycle),Maven倉庫 (Repositories),依賴管理 (Dependency Management)和專案物件模型 (Project Object Model)有一個基本的理解。
2.1 編寫POM
就像Make的Makefile、Ant的build.xml一樣,Maven專案的核心是pom.xml。POM(Project Object Model),專案物件模型定義了專案的基本資訊,用於描述專案如何構建,宣告專案依賴,等等。現在先為HelloWorld專案編寫一個最簡單的pom. xml,
首先建立一個名為helloworld的資料夾,開啟該資料夾,新建一個名為pom.xml的檔案,輸入其內容,如程式碼清單如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.hebut.test</groupId>
<artifactId>helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Maven Hello World Project</name>
</project>
XML頭
程式碼的第一行是XML頭,指定了該xml文件的版本和編碼方式。
project元素
XML頭之後緊接著就是project元素,project是所有pom.xml的根元素,它還聲明瞭一些POM相關的名稱空間及xsd元素,雖然這些屬性不是必須的,但使用這些屬效能夠讓第三方工具(如IDE中的XMl編輯器)幫助我們快速編輯POM 。
modelVersion元素
根元素下的第一個子元素modelVersion,它指定了當前POM模型的版本,對於Maven 3以及Maven 3來說,它只能是4.0.0。
座標
這段程式碼中最重要的是:包含groupId、artifactId和version的三行。這三個元素定義了一個專案基本的座標,在Maven的世界,任何的jar、pom或者war都是以基於這些基本的座標進行區分的。
groupId元素
groupId定義了專案屬於哪個組,這個組往往和專案所在的組織或公司存在關聯。譬如在googlecode上建立了一個名為myapp的專案,那麼groupId就應該是com.googlecod.myapp,如果你的公司是mycom,有一個專案為myapp。耶麼groupId就應該是com.mycom.myapp。
artifactId元素
artifactId定義了當前Maven專案在組中唯一的ID,我們為這個HelloWord專案定義artifactId為hello-world。在前面的groupld為
com.googlecode.myapp的例子中,你可能會為不同的子專案(模組)分配artifactId,如myapp-util、myapp-domain、myapp-web等。
version元素
顧名思義,version指定了Hello World專案當前的版本0.0.1。SNAPSHOT意為快照,說明該專案還處於開發中,是不穩定的版本。隨著專案的展,version會不斷更新,如升級為0.0.2、0.0.3、1.0.0等。
name元素
最後一個name元素,聲明瞭一個對於使用者更為友好的專案名稱,雖然這不是必須的,但還是推薦為每個POM宣告name。以方便資訊交流。
沒有任何實際的Java程式碼,我們就能夠定義一個Maven專案的POM,這體現了Maven的一大優點,它能讓專案物件模型最大程度地與實際程式碼相獨立,我們可以稱之為解耦,或者正交性。這在很大程度上避免了Java程式碼和POM程式碼的相互影響:比如當專案需要升級版本時,只需要修改POM。而不需要更改Java程式碼;而在POM穩定之後,日常的Java程式碼開發工作基本不涉及POM的修改。
2.2 編寫主程式碼
專案主程式碼和測試程式碼不同,專案的主程式碼會被打包到最終的構件中如:jar。而測試程式碼只在執行測試時用到,不會被打包。預設情況下,Maven假設專案主程式碼位於src/main/java目錄,我們遵循Maven的約定,建立該目錄,然後在該目錄下建立檔案org/hebut/test/helloworld/HelloWorld. java,其內容如程式碼如下所示:
package org.hebut.test.helloworld
public class HelloWorld{
public String sayHello(){
return "Hello Maven";
}
public static void main(String[] args){
System.out.println(new HelloWorld());
}
}
這是一個簡單的Java類,它有一個sayHello()方法,返回一個String同時這個類還帶有一個main方法,建立一個HelloWorld例項,呼叫sayHello()方法,並將結果輸出到控制檯。
關於該Java程式碼有兩點需要注意:首先,在絕大多數情況下,應該把專案主程式碼放到src/main/java/目錄下,而無須額外的配置,Maven會自動搜尋該目錄找到專案主程式碼。其次,該Java類的包名是org.hebut.test.helloworld,這與之前在POM中定義的groupId和artifactld相吻合。一般來說,專案中Java類的包都應該基於專案的groupld和anifactId。這樣更加清晰,更加符合邏輯,也方便搜尋構件或者Java類。
程式碼編寫完畢後,使用Maven進行編譯,在專案的根目錄下執行命令mvn clean compile會得到如下輸出:
執行完畢,發現專案根目錄多了一個target目錄:
上面使用的clean命令告訴Maven清理輸出目錄target/,compile告訴Maven編譯專案主程式碼,從輸出中看到Maven首先執行了clean:clean任務,刪除target/目錄。預設情況下,Maven構建的所有輸出都在target/目錄中;接著執行resources:resources任務,未定義專案資源,暫且略過;
最後執行compiler:compile任務,將專案主程式碼編譯至target/classes目錄,編譯好的類為:
org/hebut/test/helloworld/HelloWorld.Class
上文提到的clean:clean、resources:resources和compiler:compile對應了一些Maven外掛及外掛目標,比如clean:clean是clean外掛的clean目標,compiler:compile是compiler外掛的compile目。
至此,Maven在沒有任何額外的配置的情況下就執行了專案的清理和編譯任務。接下來,編寫一些單元測試程式碼並讓Maven執行自動化測試。
2.3 編寫測試程式碼
為了使專案結構保持清晰,主程式碼與測試程式碼應陔分別位於獨立的目錄中。正如上面所述,Maven專案中預設的主程式碼目錄是src/main/java。對應地,Maven專案中預設的測試程式碼目錄是src/test/java。因此,在編寫測試用例之前,應當先建立該目錄。
2.3.1 設定依賴
在Java世界中,由Kent Beck和Erich Gamma建立的JUnit是事實上的單元測試標準。要使用JUnit,首先需要為HelloWorld專案新增一個JUnit依賴,修改專案的POM程式碼如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.hebut.test</groupId>
<artifactId>helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Maven Hello World Project</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
dependencies元素
程式碼中添加了dependencies元素,該元素下可以包含多個dependency元素以宣告專案的依賴。
dependency元素
dependency元素用以宣告專案的依賴,這裡添加了一個依賴groupId是junit,artifactld是junit,version是4.7。前面提到groupId、artifactId和versIon是任何一個Maven專案最基本的座標。JUnit也不例外,有了這段宣告Maven就能夠自動下載junit-4.7.jar。那麼,Maven從哪裡下載這個jar 呢?
在使用Maven之前,可以去JUnit的官方網站下載分發包。有了Maven之後,它會自動訪問中央倉庫:
當然與可以自己訪問該倉庫,開啟路徑junit/junit/4. 7/,就能看到junit-4.7.pom和junit-4.7.jar。
scope元素
上述POM程式碼中還有一個值為test的元素scope,scope為依賴範圍,若依賴範圍為test則表示該依賴只對測試有效。換句話說,測試程式碼中的import JUnit程式碼是沒有問題的,但是如果在主程式碼中用import Junit程式碼,就會造成編譯錯誤。如果不宣告依賴範圍,那麼預設值就是compile,表示該依賴對主程式碼和測試程式碼都有效。
2.3.2 編寫測試程式碼
配置了測試依賴,接著就可以編寫測試類。回顧一下前面的HelloWorld類,現在要測試該類的sayHello()方法,檢查其返同值是否為"Hello Maven"。在src/test/java目錄下建立檔案,其內容程式碼如下所示:
package org.hebut.test.helloworld;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class HelloWorldTest{
@Test
public void testSayHello(){
HelloWorld helloworld=new HelloWorld();
String result=helloworld.sayHello();
assertEquals("Hello Maven",result);
}
}
一個典型的單元測試包含三個步驟:
① 準備測試類及資料;
②執行要測試的行為;
③ 檢查結果。
上述樣例,首先初始化了一個要測試的HelloWorld例項,接著執行該例項的sayHello()方法並儲存結果到result變數中,最後使用JUnit框架的Assert類檢查結果是否為我們期望的"Hello Maven"。在JUnit 3中,約定所有需要執行測試的方法都以test開頭,這裡使用了JUnit 4,但仍然遵循這一約定。在JUnit 4中,需要執行的測試方法都應該以@Test進行標註。
測試用例編寫完畢之後就可以呼叫Maven執行測試。執行mvn clean test:
我們看到compiler: testCompile任務執行成功了,測試程式碼通過編譯之後在target/test-classes下生成了二進位制檔案:
緊接著surefire:test任務執行測試,surefire是Maven中負責執行測試的外掛,這裡它執行測試用例HelloWorldTest,並且輸出測試報告,顯示一共運行了多少測試,失敗了多少,出錯了多少,跳過了多少二矽然,我們的測試通過了。
2.4 打包安裝
將專案進行編譯、測試之後,下一個重要步驟就是打包:package。 Hello WorldPOM中沒有指定打包型別,使用預設打包型別jar。簡單地執行命令:
mvn clean package進行打包,可以看到如下輸出:
類似地,Maven會在打包之前執行編譯、測試等操作,這裡看到jar: jar任務負責打包,實際上就是jar外掛的jar目標將專案主程式碼打包成一個名為helloworld-0.0.1-SNAPSHOT.jar的檔案。該檔案也位於target/輸出目錄中,它是根據artifact-version. jar規則進行命名的,如有需要,還可以使用finalName來自定義該檔案的名稱。
至此,我們得到了專案的輸出,如果有需要的話,就可以複製這個jar檔案到其他專案的Classpath中從而使用HelloVV orld類。但是,如何才能讓其他的Maven專案直接引用這個jar呢?還需要一個安裝的步驟,執行mvn clean install:
在打包之後,又執行了安裝任務install:install。從輸出可以看到該任務將專案輸出的jar安裝到了Maven本地倉庫中,可以開啟相應的資料夾看到HelloWorld專案的pom和jar:
之前講述,JUnit的POM及jar的下載的時候,我們說只有構件被下載到本地倉庫後,才能由所有Maven專案使用,這裡是同樣的道理,只有將HelloWorld的構件安裝到本地倉庫之後,其他Maven專案才能使用它。
我們已經體驗了Maven最主要的命令:mvn clean compile、mvn clean test、mvn clean package、mvn clean install。執行test之前是會先執行Compile的,執行package之前是會先執行test的。而類似地,install之前會執行package。可以在任何一個Maven專案中執行這些命令,而且我們已經清楚它們是用來做什麼的。
2.4 執行
到目前為止,還沒有執行Hello World專案,不要忘了HelloWorld類可是有一個main方法的。預設打包生成的jar是不能夠直接執行的,因為帶有main方法的類資訊不會新增到manifest中,即開啟jar檔案中的META-INF/MANIFEST. MF檔案,將無法看到Main-Class一行。預設Maven生成的JAR包只包含了編譯生成的.class檔案和專案資原始檔,而要得到一個可以直接在命令列通過java命令執行的JAR檔案,還要滿足兩個條件
■ JAR包中的/META-INF/MANIFEST.MF元資料檔案必須包含Main-Class資訊。
■ 專案所有的依賴都必須在Classpath中。
Maven有好幾個外掛能幫助使用者完成上述任務,不過用起來最方便的還是maven-shade-plugin, 它可以讓使用者配置Main-Class的值,然後在打包的時候將值填入/META-INF/MANIFEST.MF檔案。關於專案的依賴,它很聰明地將依賴JAR檔案全部解壓後,再將得到的.class檔案連同當前專案的.class檔案一起合併到最終的CLI包中,這樣,在執行CLI JAR檔案的時候,所有需要的類就都在Classpath中了。下面是一個配置樣例:
-
<build>
-
<plugins>
-
<plugin>
-
<groupId>org.apache.maven.plugins</groupId>
-
<artifactId>maven-shade-plugin</artifactId>
-
<version>1.4</version>
-
<executions>
-
<execution>
-
<phase>package</phase>
-
<goals>
-
<goal>shade</goal>
-
</goals>
-
<configuration>
-
<transformers>
-
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
-
<mainClass>org.hebut.test.helloworld.HelloWorld</mainClass>
-
</transformer>
-
</transformers>
-
</configuration>
-
</execution>
-
</executions>
-
</plugin>
-
</plugins>
-
</build>
plugin元素在POM中的相對位置應該在<project><build><plugins>下面。我們配置了mainClass為org.hebut.helloworld.HelloWorld,專案在打包時會將該資訊放到MANIFEST中。現在執行mvn clean install。待構建完成之後開啟target/目錄,可以看到helloworld-0.0.1-SNAPSHOT.jar和original-helloworld-0.0.1-SNAPSHOT.jar。前者是帶有Main-Class資訊的可執行jar,後者是原始的jar,開啟helloworld-0.0.1-SNAPSHOT.jar的META-INF/MANIFEST. MF。可以看到它包含如下資訊:
Manifest-Version: 1.0
Build-Jdk: 1.6.0_24
Built-By: hadoop
Created-By: Apache Maven 3.2.5
Main-Class: org.hebut.test.helloworld.HelloWorld
Archiver-Version: Plexus Archiver
現在,在專案根目錄中執行該jar檔案:
E:\Test\helloworld>java -jar target\helloworld-0.0.1-SNAPSHOT.jar
Hello Maven
控制檯輸出為Hello Maven,這正是我們所期望的。
三、使用Archetype生成專案骨架
3.1 Maven 專案約定
HelloWorld專案中有一些Maven的約定:在專案的根目錄中放置pom.xml,在src/main/java目錄中放置專案的主程式碼,在src/test/java中放置專案的測試程式碼。我們稱這些基本的目錄結構和pom. xml檔案內容稱為專案的骨架。Maven專案的骨架如下:
|---MavenProject
|---pom.xml
|---src
|---main
|---java
|---test
|---java
3.2 Maven Archetype
當第一次建立專案骨架的時候,你還會饒有興趣地去體會這些預設約定背後的思想,第二次,第三次,你也許還會滿意自己的熟練程度,但第四、第五次做同樣的事情,你可能就會惱火了。為此Maven提供了Archetype以幫助我們快速勾勒出專案骨架。還是以Hello World為例,我們使用maven archetype來建立該專案的骨架,離開當前的Maven專案目錄。
如果是Maven 3簡單地執行:
mvn archetype:generate
如果是Maven 2最好執行如下命令:
mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-5:generate
很多資料會讓你直接使用更為簡單的mvn archetype:generate命令,但在Maven 2中這是不安全的,因為該命令沒有指定Archetype外掛的版本,於是Maven會自動去下載最新的版本,進而可能得到不穩定的SNAPSHOT版本,導致執行失敗。然而在Maven 3中,即使使用者沒有指定版本,Maven也只會解析最新的穩定版本,因此這是安全的。
我們實際上是在執行外掛maven-archetype-plugin,注意冒號的分隔,其格式為groupld:artifactid:version:goal,org.apache.maven.plugins是maven官方外掛的groupld,maven-archetype-plugin是Archetype外掛的artifactId,2.0-alpha-5是日前該外掛最新的穩定版,generate是要使用的外掛目標。
3.3 Maven Archetype 生成HelloWorld
執行mvn archetype:generate後,緊接著會看到一段長長的輸出,有很多可用的Archetype供選擇,包括著名的Appfuse專案的Archetype、JPA專案的Archetype等。每一個Archetype前面都會對應有一個編號,同時命令列會提示一個預設的編號,其對應的Archetype為maven-archetype-quickstart,直接回車以選擇該Archetype:
,緊接著Maven會提示輸入要建立專案的groupId、artifactId、version以及包名package。如下輸入並確認:
Archetype外掛將根據我們提供的資訊建立專案骨架。在當前目錄下,Archetype外掛會建立一個名為HelloWorld,即我們定義的artifactId。從中可以看到專案的基本結構:
|---HelloWorld
|---pom.xml
|---src
|---main
|---java
|---org.hebut.maven.helloworld
|---test
|---java
基本的pom.xml已經被建立,裡面包含了必要的資訊以及一個junit依賴;主程式碼目錄src/main/java已經被建立,在該目錄下還有一個Java類org.hebut.maven.helloworld.App,注意這裡使用到了剛才定義的包名,而這個類也僅僅只有一個簡單的輸出"Hello World!"的main方法;測試程式碼目錄src/test/java也被建立好了,並且包含了一個測試用例org.hebut.maven.helloworld.AppTest。
Archetype可以幫助我們迅速地構建起專案的骨架,在前面的例子中,我們完全可以在Archetype生成的骨架的基礎上開發Hello World專案以節省大量時間。此外,這裡僅僅是看到了一個最簡單的Archetype,如果有很多專案擁有類似的自定義專案結構以及配置檔案,則完全可以一勞永逸地開發自己的Archetype。然後在這些專案中使用自定義的Archetype來快速生成專案骨架。
四、m2eclipse簡單使用
前面介紹HelloWorld專案的時候,並沒有涉及IDE,如此簡單的一個專案,使用最簡單的編輯器也能很快完成。但對於稍微大一些的專案來說,沒有IDE就是不可想象的。那麼,下面將介紹一個目前比較常用的一款Maven IDE外掛:m2eclipse。m2eclipse是Eclipse中的一款Maven外掛,功能比較強大,下面將介紹一下m2eclispe的基本使用。
4.1 匯入Maven專案
在上期,介紹瞭如何安裝m2eclipse。現在,使用m2ecilpse導人HelloWorld專案:
步驟一:選擇選單項File,然後選擇Import,我們會看到一個Import對話方塊。在該對話方塊中選擇Mavenl目錄下的Maven Projects:
步驟二:然後單擊Next按鈕,就會出現Import Projects對話方塊:
步驟三:在該對話方塊中,單擊Browse按鈕選擇HelloWorld的根目錄,即包含pom. xml檔案的那個目錄
這時對話方塊中的Projects:部分就會顯示該目錄包含的Maven專案,如上圖所示。
步驟四:單擊Finish按鈕之後,m2ecilpse就會將該專案導人到當前的workspace中
導人完成之後,就可以在Package Explorer檢視中看上圖所示的專案結構。
從上圖,我們看到主程式碼目錄src/main/java和測試程式碼目錄src/test/java成了Eclipse中的資源目錄,包和類的結構也十分清晰。當然pom.xml永遠在專案的根目錄下,而從這個檢視中甚至還能看到專案的依賴junit-3.8.1.jar,其實際的位置指向了Maven本地倉庫。這裡自定義了Maven本地倉庫地址為F:\Tools\DevelopTool\MavenSource。
4.2 建立Maven專案
建立一個Maven專案也十分簡單,下面簡單介紹一下Maven專案建立方式。
步驟一:選擇選單項File→New→Other,在彈出的對話方塊中選擇Maven下的Maven Project:
步驟二:然後單擊Next按鈕,在彈出的New Maven Project對話方塊中,使用預設的選項,不要選擇Create a simple project選項,那樣我們就能使用Maven Arche-
type
步驟三:單擊Next按鈕,此時m2eclipse會提示我們選擇一個Archetype。這裡選擇maven-ar-chetype-quickstart
步驟四:再單擊Next按鈕,由於m2eclipse實際上是在使用maven-archetype-plugin外掛建立專案,因此這個步驟與上一節使用archetype建立專案骨架類似,輸入groupld、artifactld、version、package(暫時不考慮Properties)。如下圖所示:
單擊Finish按鈕,Maven專案就建立完成了了,其結構與前一個已匯入的HelloWorld專案結構基本一致。
4.3 執行mvn命令
之前,我們需要在命令列輸入如mvn clean install之類的命令來執行maven構建,m2eclipse中也有對應的功能。在Maven專案或者pom. xml上右擊,再在彈出的快捷選單中選擇Run As,就能看到常見的Maven命令,如下圖所示:
選擇想要執行的Maven命令就能執行相應的構建,同時也能在Eclipse的console中看到構建輸出。這裡常見的一個問題是,預設選項中沒有我們想要執行的Maven命令怎麼辦?比如,預設帶有mvn test,但我們想執行mvn clean test。很簡單,選擇Maven build以自定義Maven執行命令,在彈出話框的Goals一項中輸入我們想要執行的命令,如clean test,設定一下Name,單擊Run即可。並且,下一次我們選擇Maven build或者使用快捷鍵"Alt+Shift+X,M"快速執行Maven構建的時候,上次的配置直接就能在歷史記錄中找到。