簡介
專案裡一直用的 maven,幾乎天天和這個“熟知”的工具打交道,但是,最近我發覺自己對 maven 瞭解的還不夠,例如,什麼是 goal?什麼是 phase?等等。趁著最近有時間,把官網文件大致看了一遍,並且做做筆記,也就形成了這篇部落格。
本文主要講解以下內容:
- 什麼是 maven?maven有什麼用?
- 安裝和使用 maven
- maven 的構建生命週期
- 配置 maven
- 常見問題(持續更新)
什麼是maven?maven 有什麼用?
這兩個問題,很多文章都有說到,但是,大部分都是翻譯了官網的這句籠統的話,看了和沒看一樣。
Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
以下是我的個人總結,可能稍微好理解一點。
首先,maven 是一個工具,用來幫助我們簡化、標準化專案的構建,主要分成四點:
- 如何描述一個專案。我們可以簡單地用一個座標(groupId、artifactId、version)來描述一個專案。
- 將專案的構建分為哪些階段。maven 將專案的構建過程標準化,劃分為多個有序的階段,例如,預設生命週期大致包括:編譯、測試、打包、安裝、部署等。
- 如何釋出和共享專案。maven 專案的釋出和共享基於倉庫和座標兩個基礎,我們可將專案釋出到倉庫,其他人可以通過專案的座標從倉庫中獲取這個專案。
- 如何處理專案間的關係。我們可以在 pom.xml 配置對應的座標來依賴其他的專案,而不需要手動地將眾多的 jar 包新增到 classpath 中。
下載、安裝
專案環境
maven:3.6.3
作業系統:win10
JDK:8u231
下載、安裝
進入官網下載地址,根據自己的作業系統和 JDK 選擇合適的 maven 版本,這裡我們也可以選擇下載二進位制安裝包或者原始碼包。這裡我選擇版本 3.6.3 的二進位制安裝包。
將下載的 .zip 檔案解壓,可以看到以下的目錄結構:
進行到這一步可以說 maven 已經安裝好了,只是我們還需要進行簡單的配置。
環境配置
首先,因為 maven 是由 Java 編寫,需要 JDK 才能執行,所以,我們必須保證配置好了 JAVA_HOME 的環境變數。這個我就不展開了。
然後,將解壓檔案中的 bin 目錄新增到 Path 的環境變數中(我的電腦(右鍵屬性)->高階系統設定->環境變數),如下所示:
測試
在任一位置開啟命令列,輸入:mvn -v
或mvn --version
,顯示以下內容,說明安裝完成。
簡單使用maven
安裝完成後,下面通過一個簡單的例子來模擬構建 maven 專案的過程。
生成maven專案
cmd 進入到你想要存放專案的資料夾,輸入以下命令:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
在這個命令中,archetype:generate
為一個goal
(後面展開分析),而後面那些都是執行這個goal
所需的引數。
如果是你的 maven 是剛安裝的,這個命令可以會執行比較久,因為 maven 需要將所需的軟體包或其他檔案下載到你的本地倉庫(預設在 ${user.home}/.m2/repository 目錄下)。如果出現連線超時等情況,可以嘗試多執行幾次(可以將settings.xml
的倉庫映象配置為其他地址來提高下載速度)。
執行完這個命令,可以看到指定資料夾下生成了一個 maven 專案,cd my-app
,它的檔案結構如下:
my-app
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- com
| `-- mycompany
| `-- app
| `-- App.java
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
其中,
src/main/java
目錄用來放專案的程式碼,這些程式碼將會被編譯並打包。
src/test/java
目錄用來放專案的測試程式碼,這些程式碼僅進行編譯執行,不打包。
如果我們想要新增一些資原始檔,可以在src/main
目錄下建立一個resource
目錄,這些資源最終也會被打包到專案根目錄下。
my-app
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- com
| | `-- mycompany
| | `-- app
| | `-- App.java
| `-- resources
| `-- META-INF
| `-- application.properties
`-- test
`-- java
`-- com
`-- mycompany
`-- app
`-- AppTest.java
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>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
<name>my-app</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
上面生成的專案中包含了兩個類,其中,App.java 有一個列印 Hello World! 的 main 方法。接下來我們嘗試將專案打包並執行。
構建專案
maven 將構建和部署專案的過程定義成了很多個有序的phase
(可以理解為步驟),我們可以執行以下命令來打包專案。
mvn package
這個命令將執行package
之前的phase
,如validate
、compile
、test
等,以及執行package
本身。嚴格上來講,實際上執行的不是phase
,而是繫結在這些phase
上的goal
,後面會展開講解。
執行成功後,專案根目錄生成了一個 target 資料夾,裡面就有打包好的 my-app-1.0-SNAPSHOT.jar。
my-app\target
│ my-app-1.0-SNAPSHOT.jar
│
├─classes
│ └─com
│ └─mycompany
│ └─app
│ App.class
│
├─generated-sources
│ └─annotations
├─generated-test-sources
│ └─test-annotations
├─maven-archiver
│ pom.properties
│
├─maven-status
│ └─maven-compiler-plugin
│ ├─compile
│ │ └─default-compile
│ │ createdFiles.lst
│ │ inputFiles.lst
│ │
│ └─testCompile
│ └─default-testCompile
│ createdFiles.lst
│ inputFiles.lst
│
├─surefire-reports
│ com.mycompany.app.AppTest.txt
│ TEST-com.mycompany.app.AppTest.xml
│
└─test-classes
└─com
└─mycompany
└─app
AppTest.class
執行專案
接下來就是執行 jar 包了,在命令列輸入
java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
執行完成,可以看到列印:
Hello World!
maven 的構建生命週期
構建生命週期( build lifecycle )是 maven 的核心理論基礎之一,它將專案的構建過程標準化。
maven 有三個獨立的生命週期:
- 預設生命週期。用來定義專案構建的過程。
- 清理生命週期。用來定義專案清理的過程。
- 站點生命週期。用來定義專案站點發布的過程。
phases
一個生命週期包括了許多具體的phase
(可以理解為步驟),如下:
預設生命週期
一般我們接觸比較多的是預設生命週期,它主要包括以下過程:
validate
- 校驗專案是一個正確的 maven 專案compile
- 編譯程式碼test
- 測試src/test/java
中的方法,src/test/java
的內容僅作為測試使用,不會進行打包或部署package
- 將專案打包為可執行的 jar、war 等二進位制軟體包。install
- 將軟體包安裝到本地倉庫deploy
- 將軟體包部署到遠端倉庫
除了這幾個常用的phase
,還有initialize
、generate-sources
、process-sources
等等,需要注意一點,當我們執行某個階段的命令時,類似 pre-*
, post-*
, or process-*
的階段一般只是產生中間結果,並不會對最終構建結果產生影響。
清理生命週期
pre-clean
- 在清理專案前執行一些東西
clean
- 清理專案,例如刪除 target 包
post-clean
- 在清理專案後執行一些東西
站點生命週期
pre-site
- 在生成站點文件前執行一些東西
site
- 生成站點文件
post-site
- 在生成站點文件後、部署站點文件前執行一些東西
site-deploy
- 部署站點文件
goals
在下面這個 maven 命令中,archetype:generate
稱之為一個goal
,後面那些都是執行這個goal
所需的引數。
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
至於goal
,對應的是某個外掛的某個方法,執行這個命令,我們可以在介面中看到執行的是maven-archetype-plugin
外掛的generate
方法
bindings
和上面這個命令不同,下面的這個命令我們並沒有傳入goal
,傳入的是phase
。
mvn clean
執行這個命令,可以看到,這個命令也是執行了外掛的方法,換句話來講,就是執行了goal
。
這裡就涉及到一個很重要的概念:當我們在命令中指定了phase
,執行的並不是phase
本身,而是繫結在phase
上面的goal
,繫結的goal
數量可以是一個也可以是多個。
下面是官方給的部分binding
,phase
和goal
的繫結關係主要和專案的packaging
配置有關。
Phase | plugin:goal |
---|---|
process-resources |
resources:resources |
compile |
compiler:compile |
process-test-resources |
resources:testResources |
test-compile |
compiler:testCompile |
test |
surefire:test |
package |
ejb:ejb or ejb3:ejb3 or jar:jar or par:par or rar:rar or war:war |
install |
install:install |
deploy |
deploy:deploy |
這些繫結關係,在${MAVEN_HOME}\lib\maven-core-3.6.3\META-INF\plexus\ default-bindings.xml
中定義。
mvn [phase]命令的執行
當我們執行phase
命令時,在執行指定phase
之前,會先有序地執行指定phase
之前的phase
以及它本身。例如,我執行mvn package
,會出現下面的資訊:
在package
之前的phase
,包括compile
、test
等都會被執行,而且是有序的。
settings.xml
與pom.xml
不同,settings.xml
用於全域性地配置 maven,而不是配置具體的專案。我們可以在${maven.home}/conf/
目錄下找到這個檔案。
settings.xml
檔案主要包含以下節點:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository/>
<interactiveMode/>
<offline/>
<pluginGroups/>
<servers/>
<mirrors/>
<proxies/>
<profiles/>
<activeProfiles/>
</settings>
這個檔案的配置可以參考Settings Reference。這裡我補充下servers
、mirrors
、profiles
這三個節點的內容。
servers--配置倉庫認證授權資訊
servers
用於配置倉庫(包括下載專案和部署專案的倉庫)的認證授權資訊,例如,使用者密碼等。
在具體專案中,我們可以在pom.xml
中的repositories
、pluginRepositories
和distributionManagement
節點配置用於下載專案和部署專案的倉庫,但是我們不能把認證授權的資訊放在pom.xml
檔案中,於是servers
就發揮了作用。
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
...
<servers>
<server>
<id>server001</id>
<username>my_login</username>
<password>my_password</password>
<privateKey>${user.home}/.ssh/id_dsa</privateKey>
<passphrase>some_passphrase</passphrase>
<filePermissions>664</filePermissions>
<directoryPermissions>775</directoryPermissions>
<configuration></configuration>
</server>
</servers>
...
</settings>
Mirrors--配置倉庫的映象
Mirrors
用於配置下載專案的倉庫映象。前面說過,國內使用 maven 的中央倉庫下載專案比較慢,甚至會出現超時失敗的情況,這時,我們就可以通過配置映象來提高傳輸速度。在此之前,我們需要區分映象和倉庫兩個概念,以下這篇文章作出了很好的解釋。Maven:mirror和repository 區別
在下面這個例子中,我們使用阿里雲的映象來請求 maven 的中央倉庫,注意,mirror
的mirrorOf
節點必須指定倉庫的 id,當然,這裡還支援多種形式。例如,<mirrorOf>*</mirrorOf>
表示匹配所有遠端倉庫;<mirrorOf>repo1,repo2</mirrorOf>
表示匹配倉庫 repo1 和 repo2,使用逗號分隔多個遠端倉庫;<mirrorOf>*,!repo1</miiroOf>
匹配所有遠端倉庫,repo1 除外,使用感嘆號將倉庫從匹配中排除。
<!-- settings.xml的配置 -->
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
...
<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors
...
</settings>
<!-- pom.xml的配置 -->
<project>
...
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
...
</project>
補充下,mirror
節點對repositories
、pluginRepositories
和distributionManagement
均生效。
profiles
profiles
:提供了一組可選的配置,我們可以根據不同的環境選擇啟用哪一套配置,它包括:activation
、 repositories
、pluginRepositories
和properties
四個節點。其中,activation
節點用於配置該profile
在什麼環境下才能啟用。
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
...
<profiles>
<profile>
<id>test</id>
<activation>
<activeByDefault>false</activeByDefault>
<jdk>1.5</jdk>
<os>
<name>Windows XP</name>
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
<property>
<name>mavenVersion</name>
<value>2.0.3</value>
</property>
<file>
<exists>${basedir}/file2.properties</exists>
<missing>${basedir}/file1.properties</missing>
</file>
</activation>
<properties>
<user.install>${user.home}/our-project</user.install>
</properties>
<repositories>
<repository>
<id>codehausSnapshots</id>
<name>Codehaus Snapshots</name>
<releases>
<enabled>false</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>fail</checksumPolicy>
</snapshots>
<url>http://snapshots.maven.codehaus.org/maven2</url>
<layout>default</layout>
</repository>
</repositories>
<pluginRepositories>
...
</pluginRepositories>
</profile>
...
</profiles>
...
</settings>
注意,如果settings.xml
的某個profile
被啟用,那麼,它的配置將覆蓋pom.xml
中相同 id 的倉庫以及相同名稱的 property。
pom.xml
pom.xml
幾乎包含了對 maven 專案的所有描述資訊,包括專案的座標、依賴關係、構建配置等等。這個檔案非常重要,官網有這麼一句話,在 maven 的世界裡,一個完整的專案可以不包含任何的程式碼,而只需要一個pom.xml
。
pom.xml
的節點如下:
<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>
<!-- The Basics -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>...</packaging>
<dependencies>...</dependencies>
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
<modules>...</modules>
<properties>...</properties>
<!-- Build Settings -->
<build>...</build>
<reporting>...</reporting>
<!-- More Project Information -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- Environment Settings -->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles>
</project>
關於pom.xml
檔案的內容,就不詳細展開了,可參考POM Reference。這裡介紹下兩個比較重要的概念。
scope
scope
是dependency
的子節點,用於設定以下兩個內容:
- 依賴是否在(測試)編譯、(測試)執行等時機加入 classpath。
- 限制依賴的傳遞性。(假設當前專案為 A,它依賴了 B,如果 C 依賴了 A,則 C 也會依賴 B。可以看出,A 將自己對 B 的依賴傳遞給了 C)
maven 提供了五種scope
給我們選擇,如下。
scope | 編譯期 | 執行期 | 測試編譯期 | 測試執行期 | 依賴傳遞 |
---|---|---|---|---|---|
compile | √ | √ | √ | √ | √ |
provided | √ | √ | √ | √ | × |
runtime | × | √ | × | √ | √ |
test | × | × | √ | √ | × |
system | √ | √ | √ | √ | √ |
通過dependency
的子節點 optional
可以改變傳遞性。system對應的依賴不會從倉庫獲取,而是從systemPath
指定的路徑中獲取。
super pom
pom 檔案可以通過<parent>
節點來繼承其他專案的配置資訊,而且,和 Java 的物件預設繼承 Object 一樣,pom 檔案預設會去繼承 super pom,該 pom 檔案的內容見: Super POM for Maven 3.6.3
常見問題
中央倉庫沒有的依賴,怎麼獲取
當我們的專案需要依賴某個在中央倉庫中不存在的依賴,例如,oracle
的驅動包,我們可以採用三種解決方案:
- 將依賴的專案安裝到本地倉庫。命令如下:
mvn install:install-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
- 將依賴的專案安裝到私服。命令如下:
deploy:deploy-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
- 使用
system
作用域指定包的路徑。
<dependency>
<groupId>some.group</groupId>
<artifactId>non-maven-proj</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/non-maven-proj.jar</systemPath>
</dependency>
參考資料
本文為原創文章,轉載請附上原文出處連結:https://www.cnblogs.com/ZhangZiSheng001/p/13360234.html