當前,JVM生態圈主要的三大構建工具:
- Apache Ant(帶着Ivy)
- Maven
- Gradle
對於剛開始接觸這幾個工具時,Ant是最清晰的,只要讀懂Xml配置文件你就能夠理解它幹了什麼,但是ant文件很容易變的更加複雜。Maven有自己的工程目錄規則和內置的構建生成周期,從而使構建文件更加簡單。gradle有很多開箱即用的插件,語法更加短小精悍,易於理解。
在講解maven之前這裏我們先簡單比較下Maven和Ant。下面是一個簡單的Ant例子。這個例子可以看出我們需要明確的告訴Ant。我們想讓他做什麼。有一個包含javac任務的編譯目標來講src/main/java的源碼編譯至target、class目錄。需要明確的告訴ant源碼在哪裏,結果字節碼存儲在哪裏。如何將這些字節碼打包成jar文件。
<?xml version="1.0" encoding="UTF-8"?>
<project name="test_HelloWorld" basedir="." default="">
<property name="test" location="test"/>
<target name="init">
<mkdir dir="${test}/classess/com/test"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${test}" destdir="${test}/classess/com/test"/>
</target>
<target name="dist" depends="compile">
<mkdir dir="${test}/classess/com/test/lib"/>
<jar jarfile="${test}/classess/com/test/lib/test.jar" basedir="${test}/classess/com/test"/>
</target>
<target name="run" depends="compile">
<java classname="HelloWorld" classpath="${test}/classess/com/test"/>
</target>
<target name="clean">
<delete dir="${test}/classess"/>
</target>
</project>
在Maven中你只需要創建一個簡單的pom.xml。將你的源碼放在指定目錄下。然後運行mvn install 。就能完成和ant同樣的事情。從命令行運行mvn install會處理資源文件,編譯源代碼,運行單元測試,創建一個jar。然後把這個jar安裝到本地倉庫爲其他項目提供重用性。不用做任何修改,運行mvn site然後在target/site目錄找到一個Index.html。這個文件鏈接了javaDoc和一些關於源代碼的報告。
爲什麼maven運行一個命令就能實現ant定義的一大堆的事情?
看下面我總結的兩者優缺點就明白了。
Ant
- Ant沒有正式的約定如一個一般項目的目錄結構。你必須明確告訴Ant哪裏去找源代碼,哪裏放置輸出。
- Ant是程序化,需要明確告訴的告訴Ant做什麼,什麼時候做。你必須告訴它去編譯,然後複製,然後壓縮
- Ant沒有生命週期,你必須定義目標和目標之間的依賴,你必須手工爲每個目標附上一個任務序列
Maven
- maven擁有約定,因爲你遵循了約定,它已經知道你的源代碼在哪裏,把字節碼放到target/class,然後target生成一個jar文件
- maven是聲明式的。你需要做的只是創建一個pom.xml 文件然後將源代碼放到默認目錄。Maven會幫你處理其他事情
- maven有一個生命週期,當你運行mvn install的時候被調用,這條命令告訴maven執行一系列的有序步驟。直到到達你指定的生命週期
接下來我們從以下三個方面講解Maven
- maven的pom.xml和Settings.xml解析
- maven命令
- maven的生命週期
maven的Settings.xml解析
([配置文件詳解]: http://blog.csdn.net/taiyangdao/article/list/11 )
對Maven本身行爲的定製
<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd" xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<localRepository/>
<interactiveMode/>
<offline/>
<servers/>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
<pluginGroups/>
</settings>
localRepository,給出本地庫的路徑,默認值爲${user.home}/.m2/repository。該路徑在build服務器上非常重要,項目構建過程中可以直接引用本地庫中的通用類庫。
interactiveMode,Maven執行過程中是否需要接受用戶輸入,默認值爲true。
usePluginRegistry,是否使用plugin-registry.xml文件管理Maven插件的版本,默認爲false。該文件爲用戶提供了選擇,即使用指定版本的Maven插件,而非最新版本的Maven插件。該文件是從Maven 2開始出現的,但是事實上更常用的是在POM中配置Maven插件的版本等參數,所以usePluginRegistry參數往往爲false。另外,與settings.xml文件類似,plugin-registry.xml文件也有全局和用戶之分。
offline,是否支持離線構建系統,默認值爲false。如果build服務器由於網絡或者安全等原因不能連接遠程庫,則該參數設置爲true。
-
pluginGroups,給出Maven插件所在的groupId,一個可能的groupId使用一個<pluginGroup>給出。該參數只是爲了簡化執行Maven時的參數,如爲了執行如下命令:
mvn org.mortbay.jetty:jetty-maven-plugin:run
如果在settings.xml文件中配置瞭如下<pluginGroup>:
<pluginGroups>
<pluginGroup>org.mortbay.jetty</pluginGroup>
</pluginGroups>
則可以直接執行如下命令:
mvn jetty:run
- servers,給出用以下載或部署類庫的服務器信息
- mirrors,給出指定類庫的鏡像服務器信息
- proxies,給出代理服務器信息
- profiles,給出可用的profile,這裏的profile類似於POM中的profile,但是隻包含activation, repositories, pluginRepositories和properties等與project無關的信息。
- activeProfiles,默認採用的profile,可以有多個profile。
mirrors
<mirror>
<id>mirrorId</id>
<mirrorOf>repositoryId</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>http://my.repository.com/repo/path</url>
</mirror>
- 鏡像庫的id,用以唯一標識該鏡像庫,默認default
- 鏡像庫的url,即該鏡像庫訪問位置
- 鏡像庫的name,鏡像庫的名字
- 最後,也是最重要的,是要鏡像的遠程庫。例如,如果要鏡像Maven的central庫,則設置<mirrorOf>central</mirrorOf>
對於mirrorOf參數,如果該鏡像庫的目標遠程庫不止一個,則可以使用 * 表示任意遠程庫;
external:*表示任何不在localhost和文件系統中的遠程庫;
r1,r2表示r1庫或者r2庫;
*,!r1表示除了r1庫之外的任何遠程庫。
此外,定義鏡像庫還可以提供 layout(默認default), mirrorOfLayouts(默認default,legacy)。
Servers
遠程庫通常在POM中定義,但是遠程庫所在的服務器信息,如訪問用戶名、密碼等,往往因爲不適合與POM一起發佈,所以需要在settings.xml文件中設置。
<server>
<id>deploymentRepo</id>
<username>repouser</username>
<password>repopwd</password>
<id>siteServer</id>
<privateKey>/path/to/private/key</privateKey>
<passphrase>optional; leave empty if not used.</passphrase>
</server>
- id,服務器的ID,Maven在連接一個庫或者鏡像的時候,通過id匹配要連接的服務器;
- username, password,連接服務器所需的認證信息;
- privateKey, passphrase,連接服務器所需的認證信息。privateKey默認位於${user.home}/.ssh/id_dsa;
- filePermissions, directoryPermissions,庫中的文件訪問權限和目錄訪問權限。該值的格式採用3位數字,兼容UNIX/Linux下格式;
- configuration,訪問服務器輔助要傳遞的參數,通常不必要;
Proxies
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<username>proxyuser</username>
<password>proxypass</password>
<host>proxy.host.net</host>
<port>80</port>
<nonProxyHosts>local.net|some.host.com</nonProxyHosts>
</proxy>
- id,代理的ID,默認default
- active,是否激活該代理,默認true
- protocol,代理服務器的協議,默認http
- username,password,代理服務器用戶名,密碼
- host,代理服務器的主機
- port,代理服務器的端口,默認8080
- nonProxyHosts,不使用代理服務器的域名,多個域名使用|分割
Pom.xml解析
Maven的pom.xml文件簡稱POM (Project Object Model),是Maven項目的配置和管理核心。
pom.xml文件包含大量配置信息,這些信息大致可以分爲5類。
-
POM的模型版本
<modelVersion>4.0.0</modelVersion> //說明:在Maven2和Maven3中,只支持4.0.0版本。
-
基本配置
<groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <packaging>...</packaging> <dependencies>...</dependencies> <parent>...</parent> <dependencyManagement>...</dependencyManagement> <modules>...</modules> <properties>...</properties>
-
Build配置
<build>...</build> <reporting>...</reporting>
-
環境配置
<issueManagement>...</issueManagement> <ciManagement>...</ciManagement> <mailingLists>...</mailingLists> <distributionManagement>...</distributionManagement> <scm>...</scm> <prerequisites>...</prerequisites> <repositories>...</repositories> <pluginRepositories>...</pluginRepositories> <profiles>...</profiles>
- issueManagement,給出defect tracking system及其訪問URL
- system
- url
- ciManagement,給出Continuous Integration Management、其URL和notifier
- system
- url
- notifiers,集成過程中發生事件,以某種方式(如mail)通知開發人員
- scm,software configuration management
- connection,用戶使用的URI,能夠只讀地訪問版本控制系統
- developerConnection,開發人員使用URI,能夠讀寫地訪問版本控制系統
- tag,項目當前的tag
- url,可通過Web瀏覽器訪問的公共網址
- distributionManagement,構件的發佈管理,詳情見後續文章
- prerequisites,POM執行的前提條件,目前只支持對Maven版本的要求
- maven
- mailingLists,開發人員或用戶的郵件列表
- name
- subscribe,訂閱地址
- unsubscribe,取消訂閱地址
- post,POST郵件的目的地址
- archive,打包的郵件列表歷史記錄
- otherArchives,鏡像打包的郵件列表歷史記錄
-
其他信息
<name>...</name> <description>...</description> <url>...</url> <inceptionYear>...</inceptionYear> <licenses>...</licenses> <organization>...</organization> <developers>...</developers> <contributors>...</contributors>
name,項目的名稱代號
description,項目的說明
url,項目的官網URL
inceptionYear,項目的開發年份
-
licenses,項目使用的License。其中可以包含多個license,license具體又包含如下子屬性
- name,license的名稱
- url,license可訪問的URL地址
- distribution,license發佈的方式。repo表示可以直接從Maven庫下載,manual表示必須手工安裝
- comments,對license的說明
organization,包含組織的name,組織的官網url
developers,其中的developer包含id, name, email, url, organization, organizationUrl, roles, timezone, properties屬性(properties是可以自定義的各種必要屬性)
contributors,其中的contributor包含與developer基本相同的屬性,除了沒有id屬性之外
packaging
packaging給出了項目的打包類型,即作爲項目的發佈形式,其可能的類型。在Maven 3中,其可用的打包類型如下:
• jar,默認類型
• war
• ejb
• ear
• rar
• par
• pom
• maven-plugin
multi-modules
Maven 3支持Maven項目的多模塊(multi-modules)結構。這樣的Maven項目也被稱爲聚合項目,通常由一個父模塊和若干個子模塊構成。
其中,父模塊必須以pom打包類型,同時以<modules>給出所有的子模塊。父模塊的POM示例(其中的每個module,都是另外一個Maven項目)
...
<packaging>pom</packaging>
<modules>
<module>my-frontend-project</module>
<module>my-service-project</module>
<module>my-backend-project</module>
</modules>
...
Maven項目的繼承
Maven項目之間不僅存在多模塊的聚合關係,而且Maven項目之間還可以存在相互繼承的關係。Maven項目之間的繼承關係通過<parent>表示,在子Maven項目的POM中配置示例如下:
<parent>
<groupId>com.ericsson.jcat</groupId>
<artifactId>jcat-bundle</artifactId>
<version>2.0</version>
<relativePath>../jcat-bundle</relativePath>
</parent>
其中的relativePath給出父項目相對於子項目的路徑,這樣在構件子項目時首先從該相對路徑查找父項目,如果沒有才會從本地庫或進而遠程庫中查找父項目。
在子項目中,能夠繼承父項目的如下配置:
• dependencies
• developers
• contributors
• plugin lists
• reports lists
• plugin executions with matching ids
• plugin configuration
dependencies
Maven項目的構建往往要依賴於第三方的類庫。通過<dependencies>可以給出Maven項目所依賴的第三方類庫
<dependencies>
<dependency>
<groupId>org.gitlab</groupId>
<artifactId>java-gitlab-api</artifactId>
<version>1.2.6</version>
</dependency>
</dependencies>
- 對於一個依賴<dependency>,首先要給出被依賴的Maven構件(被依賴的只能是Maven構件)的具體標識信息,如groupId、artifactId和version(可以是一個範圍)。爲了進一步區分Maven構件的內容(如source、bin和doc),往往還會給出Maven構件的classifier。
- type,打包類型,默認jar
- scope,被依賴的Maven構件在classpath中的可訪問範圍
- compile,默認值,被依賴的Maven構件在compile、runtime和test的時候都可以在classpath中找到
- provided,被依賴的Maven構件在compile和test的時候都可以在classpath中找到,在runtime的時候由JDK或容器提供
- system,被依賴的Maven構件在compile和test的時候都可以在classpath中找到,在runtime的時候必須顯式將JAR加入到classpath中
- runtime,被依賴的Maven構件在runtime和test的時候都可以在classpath中找到,在compile時不是必須的
- test,被依賴的Maven構件在test的時候可以在classpath中找到,在compile和runtime時不是必須的
- systemPath,只有當<scope>system</scope>時才設置,否則構建時會報錯。該值必須是一個絕對路徑,可以通過環境變量給出具體的絕對路徑
- optional,當前Maven項目的構件被其他項目依賴,此處被依賴的Maven構件相對於其他項目來說是不必須的
- exclusions,將一個被依賴的Maven構件中的部分類庫,從classpath中去掉
Properties
在Maven的pom.xml文件中,<properties>用於定義全局變量,在POM中通過${property_name}的形式引用變量的值。
POM中的全局變量可以分爲如下5種類型:
• 系統Shell的環境變量env.property_name,如${env.PATH}表示引用當前系統的PATH變量值,注意這裏的PATH必須都是大寫。
• Java System Properties,即Java屬性文件,如${java.home}
• project.property_name,直接引用POM中的元素值,如${project.version}表示引用<project><version>1.0</version></project>中的1.0
• settings.property_name,直接引用settings.xml中的元素值,如${settings.offline}表示引用<settings><offline>false</offline></settings>中的false
• property_name,直接訪問<properties>中已經定義的變量值,如${myVar}表示引用<properties><myVar>myvalue</myVar></properies>中的myvalue
build
<resources>
資源往往不是代碼,無需編譯,而是一些properties或XML配置文件,構建過程中會往往會將資源文件從源路徑複製到指定的目標路徑。
<plugins>
<plugins>給出構建過程中所用到的插件。
<pluginManagement>
在<build>中,<pluginManagement>與<plugins>並列,兩者之間的關係類似於<dependencyManagement>與<dependencies>之間的關係。<pluginManagement>中也配置<plugin>,其配置參數與<plugins>中的<plugin>完全一致。只是,<pluginManagement>往往出現在父項目中,其中配置的<plugin>往往通用於子項目。子項目中只要在<plugins>中以<plugin>聲明該插件,該插件的具體配置參數則繼承自父項目中<pluginManagement>對該插件的配置,從而避免在子項目中進行重複配置。
Project Build特有的<extensions>
<extensions>是執行構建過程中可能用到的其他工具,在執行構建的過程中被加入到classpath中。
也可以通過<extensions>激活構建插件,從而改變構建的過程。
通常,通過<extensions>給出通用插件的一個具體實現,用於構建過程。
reporting
<reporting>中的配置作用於Maven的site階段,用於生成報表。<reporting>中也可以配置插件<plugins>,並通過一個<plugin>的<reportSet>爲該插件配置參數。注意,對於同時出現在<build>和<reporting>中的插件,<reporting>中對該插件的配置也能夠在構建過程中生效,即該插件的配置是<build>和<reporting>中的配置的合併。
Profiles
在Maven項目中,profile是根據不同的構件環境,對構建(build)過程進行動態配置的手段。
可以通過pom.xml定義多個profile,也可以通過settings.xml文件定義多個profile。對於一個profile,如果同時在pom.xml和settings.xml中配置,則settings.xml中的配置優先。
settings.xml中的profile只能配置如下元素:
• id
• activation
• repositories
• pluginRepositories
• properties
pom.xml中的profile能配置如下元素:
• id
• activation
• repositories
• pluginRepositories
• properties
• build
• modules
• dependencies
• dependencyManagement
• repositories
• pluginRepositories
• distributionManagement
• reporting
在每個profile中,都有一個<activation>給出當前profile被激活的環境條件,其中給出的任何一個條件滿足即激活該profile
Maven生命週期
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html(官網解說)
Maven強大的一個重要的原因是它有一個十分完善的生命週期模型(lifecycle),這個生命週期可以從兩方面來理解,第一,顧名思義,運行Maven的每個步驟都由它來定義的,這種預定義的默認行爲使得我們使用Maven變得簡單,相比而言,Ant的每個步驟都要你手工去定義。第二,這個模型是一種標準,在不同的項目中,使用Maven的接口是一樣的,這樣就不用去仔細理解每個項目的構建了,一般情況下,mvn clean install 這樣的命令是通用的。
Maven有三套相互獨立的生命週期,請注意這裏說的是“三套”,而且“相互獨立”,很多人容易將Maven的生命週期看成一個整體,其實不然。這三套生命週期分別是:
- Clean Lifecycle 在進行真正的構建之前進行一些清理工作。
- Default Lifecycle 構建的核心部分,編譯,測試,打包,部署等等。
- Site Lifecycle 生成項目報告,站點,發佈站點。
每套生命週期都由一組階段(Phase)組成,我們平時在命令行輸入的命令總會對應於一個特定的階段。比如,運行mvn clean ,這個的clean是Clean生命週期的一個階段。
Clean生命週期一共包含了三個階段:
• pre-clean 執行一些需要在clean之前完成的工作
• clean 移除所有上一次構建生成的文件
• post-clean 執行一些需要在clean之後立刻完成的工作
mvn clean 中的clean就是上面的clean,在一個生命週期中,運行某個階段的時候,它之前的所有階段都會被運行,也就是說,mvn clean 等同於 mvn pre-clean clean ,如果我們運行 mvn post-clean ,那麼 pre-clean,clean 都會被運行。這是Maven很重要的一個規則,可以大大簡化命令行的輸入。
下面看一下Site生命週期的各個階段:
• pre-site 執行一些需要在生成站點文檔之前完成的工作
• site 生成項目的站點文檔
• post-site 執行一些需要在生成站點文檔之後完成的工作,並且爲部署做準備
• site-deploy 將生成的站點文檔部署到特定的服務器上
這裏經常用到的是site階段和site-deploy階段,用以生成和發佈Maven站點,這可是Maven相當強大的功能,Manager比較喜歡,文檔及統計數據自動生成,很好看。
Maven的最重要的Default生命週期
• validate
• generate-sources
• process-sources
• generate-resources
• process-resources 複製並處理資源文件,至目標目錄,準備打包。
• compile 編譯項目的源代碼。
• process-classes
• generate-test-sources
• process-test-sources
• generate-test-resources
• process-test-resources 複製並處理資源文件,至目標測試目錄。
• test-compile 編譯測試源代碼。
• process-test-classes
• test 使用合適的單元測試框架運行測試。這些測試代碼不會被打包或部署。
• prepare-package
• package 接受編譯好的代碼,打包成可發佈的格式,如 JAR 。
• pre-integration-test
• integration-test
• post-integration-test
• verify
• install 將包安裝至本地倉庫,以讓其它項目依賴。
• deploy 將最終的包複製到遠程的倉庫,以讓其它開發人員與項目共享。
記住,運行任何一個階段的時候,它前面的所有階段都會被運行,這也就是爲什麼我們運行mvn install 的時候,代碼會被編譯,測試,打包。
Maven常用命令
Maven參數
- -D 傳入屬性參數
- -P 使用pom中指定的配置
- -e 顯示maven運行出錯的信息
- -o 離線執行命令,即不去遠程倉庫更新包
- -X 顯示maven允許的debug信息
- -U 強制去遠程參考更新snapshot包
Maven常用命令
創建Maven的普通java項目:
mvn archetype:create -DgroupId=packageName -DartifactId=projectName創建Maven的Web項目:
mvn archetype:create -DgroupId=packageName -DartifactId=webappName-DarchetypeArtifactId=maven-archetype-webapp編譯源代碼:mvn compile
編譯測試代碼:mvn test-compile
運行測試:mvn test
產生site:mvn site
打包:mvn package
在本地Repository中安裝jar:mvn install
清除產生的項目:mvn clean
生成eclipse項目:mvn eclipse:eclipse
生成idea項目:mvn idea:idea
組合使用goal命令,如只打包不測試:mvn -Dtest package
編譯測試的內容:mvn test-compile
只打jar包: mvn jar:jar
-
只測試而不編譯,也不測試編譯:
mvn test -skipping compile -skipping test-compile ( -skipping 的靈活運用,當然也可以用於其他組合命令)
清除eclipse的一些系統設置:mvn eclipse:clean
ps:一般使用情況是這樣,首先通過cvs或svn下載代碼到本機,然後執行mvn eclipse:eclipse生成ecllipse項目文件,然後導入到eclipse就行了;修改代碼後執行mvn compile或mvn test檢驗,也可以下載eclipse的maven插件。mvn -version/-v 顯示版本信息
mvn archetype:generate 創建mvn項目
mvn archetype:create -DgroupId=com.oreilly -DartifactId=my-app 創建mvn項目
mvn package 生成target目錄,編譯、測試代碼,生成測試報告,生成jar/war文件
mvn jetty:run 運行項目於jetty上,
mvn compile 編譯
mvn test 編譯並測試
mvn clean 清空生成的文件
mvn site 生成項目相關信息的網站
mvn -e 顯示詳細錯誤 信息.
mvn validate 驗證工程是否正確,所有需要的資源是否可用。
mvn test-compile 編譯項目測試代碼。 。
mvn integration-test 在集成測試可以運行的環境中處理和發佈包。
mvn verify 運行任何檢查,驗證包是否有效且達到質量標準。
mvn generate-sources 產生應用需要的任何額外的源代碼,如xdoclet。
發佈第三方Jar到本地庫中:
mvn install:install-file -DgroupId=com -DartifactId=client -Dversion=0.1.0 -Dpackaging=jar -Dfile=d:\client-0.1.0.jar
-DdownloadSources=true
-DdownloadJavadocs=true