所有項目的構(gòu)建都是有生命周期的,這個生命周期包括:項目清理、初始化、編譯、測試、打包、集成測試、驗證、部署、站點生成等幾乎所有的構(gòu)建步驟。就像人的生命周期都有出生、少年、青年、壯年、老年、死亡這些階段,而在這些階段我們都可以做不同的事情,比如青年階段就可以結(jié)婚了。
Maven作為項目構(gòu)建工具,它也具有這樣的生命周期。比如執(zhí)行mvn package就表示執(zhí)行默認生命周期階段package。maven的生命周期是抽象的,生命周期中包含多個有次序的階段,每個階段都可能會執(zhí)行多個行為,而實際行為都是由插件來完成的,如package階段的任務可能就是由maven-jar-plugin插件完成,在maven中生命周期和插件兩者協(xié)同工作,密不可分。
1Maven生命周期設(shè)計思想(模板方法模式)
- Maven的生命周期是抽象的,這意味著生命周期本身不做任何實際工作,在maven的設(shè)計中,實際的任務(如編譯源代碼)都交由插件來完成。這種設(shè)計思想如設(shè)計模式中的模板方法相似。
- 模板方法模式在父類中定義算法的整體結(jié)構(gòu),子類可以通過實現(xiàn)或者重寫父類的方法來控制實際的行為,這樣既保證了算法有足夠的可擴展性,又嚴格控制算法的整體結(jié)構(gòu),如下模板方法抽象類能夠很好地體現(xiàn)Maven聲明周期概念:
package com.cnu.offline.template.method;
public abstract class AbstractBuild {
/**
* 項目構(gòu)建
*/
public void build(){
initialize();
compile();
test();
packagee();
integrationTest();
deploy();
}
protected abstract void initialize();
protected abstract void compile();
protected abstract void test() ;
protected abstract void packagee();
protected abstract void integrationTest();
protected abstract void deploy();
}
- 這段代碼非常簡單,build()方法定義了整個構(gòu)建過程,依次初始化、編譯、測試、打包、集成測試、部署行為,它們都交由子類去實現(xiàn)。上述代碼和maven雖然相差甚遠,但是這種思想和maven很相似。
- maven的生命周期更多更復雜。maven聲明周期抽象了構(gòu)建的各個步驟,定義了次序,但是沒有提供具體實現(xiàn),那么誰來實現(xiàn)呢?插件。生命周期的每個階段都可以綁定一個或者多個插件行為,而且maven為大多數(shù)構(gòu)建步驟綁定了默認的插件。比如:針對編譯的插件有maven-compileer-plugin,針對測試的插件有maven-surefire-plugin等。
- maven的生命周期和插件機制一方面保證了所有maven項目都有一致的構(gòu)建標準(步驟)。另一方面有通過默認插件簡化和穩(wěn)定了實際項目的構(gòu)建,此外,該機制還提供了足夠的擴展空間,用戶可以通過配置現(xiàn)有插件或者自行編寫插件來完成自定義構(gòu)建行為。
2生命周期詳解
2.1三套生命周期
- 項目構(gòu)建的生命周期一般包括:項目清理、初始化、編譯、測試、打包、集成測試、驗證、部署、站點生成等階段,但是maven為了更方便構(gòu)建,maven使用了3套相互獨立的生命周期來包含這些階段,名稱分別為:clean、default、site。clean生命周期的目的是清理項目,default生命周期目的是構(gòu)建項目,site生命周期是建立項目站點。3套生命周期相互獨立。每套生命周期中各個階段都是由次序關(guān)系,后面階段執(zhí)行前必須先執(zhí)行前面的階段,就像人在死亡前他必須經(jīng)歷出生、少年、青年、壯年、老年這些階段。
- 每個生命周期都包含多個階段,但是maven默認不是在所有階段上都綁定了插件行為,只是在關(guān)鍵的周期階段綁定了插件行為,沒綁定插件行為的階段在構(gòu)建時就不會執(zhí)行
2.2clean生命周期
- clean生命周期的目的是清理項目,包含階段:
- 1 pre-clean 執(zhí)行一些清理前需要完成的工作。
- 2 clean(綁定默認插件目標maven-clean-plugin:clean) 清理上一次構(gòu)建生成的文件(target/目錄)
- 3 post-clean 執(zhí)行一些清理后需要完成的工作。
2.3default生命周期
default生命周期定義了真正構(gòu)建時所需要執(zhí)行的所有步驟,最核心生命周期。包含階段,默認綁定的插件與打包類型packaging有關(guān),下面以jar為例:
1.validate:validate the project is correct and all necessary information is available.
2.initialize:initialize build state, e.g. set properties or create directories.
3.generate-sources:generate any source code for inclusion in compilation.
4.process-sources :process the source code, for example to filter any values.
5.genrate-resources:generate resources for inclusion in the package.
6.process-resources(綁定默認插件目標maven-resources-plugin:resources)處理項目主資源文件,一般來說,是對src/main/resources目錄的內(nèi)容進行變量替換等工作后,然后復制到項目輸出的主classpath目錄中。
7.compile(綁定默認插件目標maven-compiler-plugin:compile)編譯項目的主源碼,編譯src/main/java目錄下的Java文件至項目輸出的主classpath目錄中。
8.process-classes:post-process the generated files from compilation, for example to do bytecode enhancement on Java classes.
9.generate-test-sources:generate any test source code for inclusion in compilation.
10.process-test-sources:process the test source code, for example to filter any values.
11.generate-test-resources:create resources for testing.
12.process-test-resources(綁定默認插件目標maven-resources-plugin:testResources)處理項目測試資源文件,一般來說,是對src/test/resources目錄的內(nèi)容進行變量替換等工作后,然后復制到項目輸出的測試classpath目錄中。
13.test-compile(綁定默認插件目標maven-compiler-plugin:testCompile)編譯項目的測試代碼。一般來說,是編譯src/test/java目錄下的Java文件至項目輸出的測試classpath目錄中。
14.process-test-classes:post-process the generated files from test compilation, for example to do bytecode enhancement on Java classes. For Maven 2.0.5 and above.
15.test(綁定默認插件目標maven-surefire-plugin:test)使用單元測試框架運行測試,測試代碼不會被打包部署。
16.prepare-package:perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package. (Maven 2.1 and above)
17.package(綁定默認插件目標maven-jar-plugin:jar)接受編譯好的代碼,打包成可發(fā)布的格式,如JAR
18.pre-integraton-test:perform actions required before integration tests are executed. This may involve things such as setting up the required environment.
19.integration-test:process and deploy the package if necessary into an environment where integration tests can be run.
20.post-integration-test: perform actions required after integration tests have been executed. This may including cleaning up the environment.
21.verify:run any checks to verify the package is valid and meets quality criteria.
22.install(綁定默認插件目標maven-install-plugin:install)將包安裝到Maven倉庫,供本地項目使用
23.deploy(綁定默認插件目標maven-deploy-plugin:deploy)將最終的包復制到遠程倉庫,供其他開發(fā)人員和maven項目使用。部署至遠程倉庫配置
執(zhí)行mvn命令到某一個生命周期
mvn clean test-compile
2.4site生命周期
- site生命周期是建立和發(fā)布項目站點,maven能夠基于POM所包含的信息自動生成一個友好的站點,方便團隊交流和發(fā)布項目信息。
- 1.pre-site : execute processes needed prior to the actual project site generation
- 2.site :(綁定默認插件目標maven-site-plugin:site) generate the project's site documentation
- 3.post-site :execute processes needed to finalize the site generation, and to prepare for site deployment
- 4.site-deploy:(綁定默認插件目標maven-site-plugin:deploy) deploy the generated site documentation to the specified web server
2.5命令行與生命周期
從命令行執(zhí)行Maven任務最主要的方式就是調(diào)用Maven聲明周期階段。
$mvn clean:該命令調(diào)用clean生命周期的clean階段,實際執(zhí)行的階段為clean生命周期的pre-clean 和clean階段
$mvn clean deploy site-deploy:該命令調(diào)用clean生命周期的clean階段、default周期的deploy階段、site生命周期的site-deploy階段。實際執(zhí)行clean生命周期pre-clean,clean 階段,default生命周期所有階段,site生命周期所有階段。
執(zhí)行mvn clean install所執(zhí)行的任務。
E:\Repository\iqasproject\iqasweb>mvn clean install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building iqasweb Maven Webapp 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ iqasweb ---
[INFO] Deleting E:\Repository\iqasproject\iqasweb\target
[INFO]
[INFO] --- maven-resources-plugin:2.7:resources (default-resources) @ iqasweb --
-
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 14 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.3:compile (default-compile) @ iqasweb ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 388 source files to E:\Repository\iqasproject\iqasweb\target\cl
asses
[INFO]
[INFO] --- maven-resources-plugin:2.7:testResources (default-testResources) @ iq
asweb ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory E:\Repository\iqasproject\iqasweb\src
\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.3:testCompile (default-testCompile) @ iqasweb
---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 8 source files to E:\Repository\iqasproject\iqasweb\target\test
-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.19:test (default-test) @ iqasweb ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ iqasweb ---
[INFO] Packaging webapp
[INFO] Assembling webapp [iqasweb] in [E:\Repository\iqasproject\iqasweb\target\
iqasweb]
[INFO] Processing war project
[INFO] Copying webapp resources [E:\Repository\iqasproject\iqasweb\src\main\weba
pp]
[INFO] Webapp assembled in [7681 msecs]
[INFO] Building war: E:\Repository\iqasproject\iqasweb\target\iqasweb.war
[WARNING] Warning: selected war files include a WEB-INF/web.xml which will be ig
nored
(webxml attribute is missing from war task, or ignoreWebxml attribute is specifi
ed as 'true')
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ iqasweb ---
[INFO] Installing E:\Repository\iqasproject\iqasweb\target\iqasweb.war to D:\Sof
t\maven\maven_jar\repository\com\cnu\iqas\iqasweb\0.0.1-SNAPSHOT\iqasweb-0.0.1-S
NAPSHOT.war
[INFO] Installing E:\Repository\iqasproject\iqasweb\pom.xml to D:\Soft\maven\mav
en_jar\repository\com\cnu\iqas\iqasweb\0.0.1-SNAPSHOT\iqasweb-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
3插件目標
- Maven的核心僅僅定義了抽象生命周期,具體工作由插件完成,對于插件本身,為了能夠?qū)崿F(xiàn)代碼復用,它往往能完成多個任務,也就具有多個功能,這些功能聚集在一個插件中,每個功能就叫做一個插件目標。
- maven-dependency-plugin插件有十多個目標,每個目標對應一個功能,目標有:dependency:analyze、dependency:tree、dependency:list。這是一種通用寫法,冒號前面是插件前綴,后面是插件目標
4插件綁定
我們把插件的目標綁定到maven生命周期的某個階段用于完成實際任務,叫做插件綁定。例如:maven-compiler-plugin這一插件的compile目標能夠完成項目編譯這一任務,因此就把該目標綁定到default生命周期的compile這一階段。
4.1內(nèi)置綁定
Maven在核心的生命周期階段綁定了很多插件的目標,當用戶通過命令調(diào)用生命周期階段的時候?qū)牟寮繕司蜁?zhí)行相應的任務。
對于默認的插件綁定在上面介紹3個生命周期時已經(jīng)有備注了,下面對default生命周期的內(nèi)置插件綁定關(guān)系做一些說明。
default生命周期與插件的綁定關(guān)系由打包類型決定packaging,因為jar項目需要打包成JAR包,war項目需要打包成war包,還有pom、maven-plugin、ear等類型。
default生命周期插件目標綁定關(guān)系
4.2自定義綁定
除了內(nèi)置綁定外,用戶還可以選擇將某個插件目標綁定到生命周期的某個階段。
需求:創(chuàng)建項目源碼包
*內(nèi)置的插件沒有涉及這一任務,需要自己配置。
- 1.確定插件目標:
去哪里找插件?基本上所有的插件插件都來源于Apache和Codehaus,
首先通過查看插件發(fā)現(xiàn)maven-source-plugin插件的jar-no-fork目標能夠?qū)㈨椖康?strong>主代碼(包括主資源)打包成jar文件, - 2.確定生命周期階段:
查看生命周期各階段介紹,發(fā)現(xiàn)綁定到default生命周期的verify階段上,在執(zhí)行完集成測試后和安裝構(gòu)件之前創(chuàng)建源碼jar包。 - 在pom.xml中配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>my-attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
解釋
groupId為org.apache.maven.plugins(maven官方插件),凡是gourpId為org.apache.maven.plugins可以省略(不推薦).
executions下每個execution元素可以用來配置執(zhí)行一個任務,該例中配置一個id為my-attach-sources的任務。
通過phase將其綁定到verify生命周期階段上。不配置phase也會執(zhí)行目標(不推薦),因為該插件編寫時默認階段是package,而package在install之前.
通過goals配置指定要執(zhí)行的插件目標。
執(zhí)行mvn clean install,多了如下兩行,生成了源碼包iqasweb-sources.jar。
[INFO] --- maven-source-plugin:2.1.1:jar-no-fork (my-attach-sources) @ iqasweb ---
[INFO] Building jar: E:\Repository\iqasproject\iqasweb\target\iqasweb-sources.jar
- 多個目標被綁定到同一目標上時,執(zhí)行順序安裝聲明先后執(zhí)行。
5插件配置
插件和生命周期綁定后,用戶可以配置插件和插件目標的參數(shù),進一步刁征插件目標所執(zhí)行的任務,以滿足項目需求,幾乎所有的Maven插件都有一些參數(shù)。
用戶可以通過命令行或者POM來配置插件參數(shù),注意,命令行和POM配置的參數(shù)的名稱一般都不一樣。就需要查看插件的信息,下面有介紹。
配置之前需要查看插件有哪些參數(shù)可以配置,配置參數(shù)的意義。
5.1命令行插件配置
- 很多插件參數(shù)都支持命令行配置。但不是所有。
- 在Maven命令中使用-D參數(shù),并伴隨一個key=value的形式來配置插件目標參數(shù)。
案例
maven-surefire-plugin提供了一個maven.test.skip的命令行參數(shù)(在pom中參數(shù)為skip),當其值為true時會跳過執(zhí)行測試。
$mvn install -Dmaven.test.skip=true
5.2POM中插件全局配置
對于參數(shù)很少改變的配置應該配置在pom中。
對插件的配置有全局配置和對插件目標的配置。全局配置就是所有基于該插件目標的任務,都會使用這些配置。對插件目標配置就只是對該目標的任務執(zhí)行該配置。
案例
- 配置maven-compiler-plugin告訴它編譯java1.7版本的源文件,生成與JVM1.7兼容的字節(jié)碼文件。
- 配置之前查看插件有哪些全局配置參數(shù)參數(shù)配置。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<!-- if you want to use the Java 8 language features (-source 1.8) and also want the compiled classes to be compatible with JVM 1.8 (-target 1.8), -->
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
- 全局配置在plugin下面的configuration
- 這樣不管綁定到compile階段的maven-compiler-plugin:compile任務,還是綁定到testCompiler階段maven-compiler-plugin:testCompiler任務,就都是用該配置。
5.3POM中插件任務配置
- 為插件的某個目標配置參數(shù),配置在plugin/executions/execution下面的configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<executions>
<execution>
<id>my-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</execution>
</executions>
</plugin>
6獲取插件信息
去哪里尋找合適的插件?,詳細了解插件的配置點?
maven插件很多,大部分沒有完善的文檔,一般maven官方的插件文檔比較完善一點。
獲取插件信息有兩種,取網(wǎng)站上查看插件信息,如果網(wǎng)站信息沒有,就通過調(diào)用命令行來獲取插件的描述信息。
6.1在線插件信息
-
以maven官方插件查看為例。查看maven-surefire-plugin為例。如下圖:
maven-surefire-plugin插件官方介紹
一般來說,通過閱讀插件文檔中的使用介紹和實例就可以很好的使用插件了,如果想要了解目標參數(shù),就需要訪問插件的每個目標的文檔。
6.2使用maven-help-plugin描述插件
- maven-help-plugin插件的describe目標可以查看插件的詳細信息。更多目標
- 用-D參數(shù),并伴隨一個key=value的形式來配置查詢參數(shù)。
常見使用方法
案例:查看maven-compiler-plugin的信息
方法1:
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin:2.1
Name: Maven Compiler Plugin
Description: The Compiler Plugin is used to compile the sources of your
project.
Group Id: org.apache.maven.plugins
Artifact Id: maven-compiler-plugin
Version: 2.1
Goal Prefix: compiler
This plugin has 3 goals:
compiler:compile
Description: Compiles application sources
compiler:help
Description: Display help information on maven-compiler-plugin.
Call
mvn compiler:help -Ddetail=true -Dgoal=<goal-name>
to display parameter details.
compiler:testCompile
Description: Compiles application test sources.
方法2:
使用前綴,maven-compiler-plugin的前綴為compiler,在描述插件時可以省略版本信息,maven就會獲取最新版的進行表達:
mvn help:describe -Dplugin=compiler
方法3:
如果想輸出更詳細信息,包括插件每個目標的參數(shù),加上detail
mvn help:describe -Dplugin=compiler -Ddetail.
7從命令行調(diào)用插件
如果在命令行運行mvn -h來顯示mvn命令幫助,就可以看到如下信息:告訴了mvn命令的基本用法
usage: mvn [options] [<goal(s)>] [<phase(s)>]
- options:表示可用的選項,mvn有20多個。如 mvn -v
- phase生命周期階段,如 mvn clean install
- goal插件目標,支持直接從命令行調(diào)用插件目標。因為有些任務不適合綁定在生命周期上,如maven-help-plugin:describe,我們不需要再構(gòu)建項目的時候去顯示依賴樹。
- 調(diào)用案例如:mvn dependency:tree
mvn help:describe -Dplugin=compiler
help是maven-help-plugin插件的前綴。
- 調(diào)用案例如:mvn dependency:tree
8插件解析機制
8.1插件倉庫
- 與依賴構(gòu)件一樣,插件構(gòu)件同樣基于坐標存在于Maven倉庫中,需要時從本地倉庫尋找,不存在,從遠程倉庫下載到本地使用。
- 插件倉庫使用pluginRepositories和pluginRepository配置,和依賴的中央倉庫類似,Maven在超級POM中同樣內(nèi)置了插件遠程倉庫配置:
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
- 當然maven配置的中央插件倉庫已經(jīng)能滿足我們需求,但是如果使用的插件找不到,也可以配置其它,在POM中或者settings.xml(在settings.xml配置在nexus私服中介紹)中加入其它插件倉庫配置。
8.2插件的默認groupId
- 在pom中配置插件時,如果該插件時maven官方插件(即groupid=org.apache.maven.plugins),可以省略(不推薦)。
8.3解析插件版本。
- 同樣為了簡化插件配置和使用,在用戶沒有提供插件版本情況下,maven會自動解析插件版本。
-
情況1:核心插件。超級POM中有配置。
超級POM中為所有核心插件設(shè)定了版本,所有maven項目都會繼承超級pom,所以在maven項目中使用核心插件的版本已經(jīng)確定了。 -
情況2:非核心插件,超級POM中沒有配置
使用的插件不是核心插件,超級POM中沒有配置,maven會去所有倉庫尋找可用版本,然后選擇release版,即最新發(fā)布版(而不是latest最新版,最新版有可能是快照版)
8.4插件前綴
- 前面使用mvn命令時支持插件前綴來簡化插件的調(diào)用,現(xiàn)在解釋如何根據(jù)插件前綴解析得到插件的坐標。
- 插件前綴與groupId:artifactId是一一對應的,這種匹配關(guān)系存儲在倉庫元數(shù)據(jù)中。
有什么不懂的一起探討一下吧,我也是在學習的路上。喜歡給我點個贊吧(哈哈),我會繼續(xù)努力的。