關於maven的一份小筆記
阿新 • • 發佈:2020-07-22
# 簡介
專案裡一直用的 maven,幾乎天天和這個“熟知”的工具打交道,但是,最近我發覺自己對 maven 瞭解的還不夠,例如,什麼是 goal?什麼是 phase?等等。趁著最近有時間,把[官網文件](https://maven.apache.org/)大致看了一遍,並且做做筆記,也就形成了這篇部落格。
本文主要講解以下內容:
1. 什麼是 maven?maven有什麼用?
2. 安裝和使用 maven
3. maven 的構建生命週期
4. 配置 maven
5. 常見問題(持續更新)
# 什麼是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 是一個工具,用來幫助我們**簡化**、**標準化**專案的構建,主要分成四點:
1. **如何描述一個專案**。我們可以簡單地用一個座標(groupId、artifactId、version)來描述一個專案。
2. **將專案的構建分為哪些階段**。maven 將專案的構建過程標準化,劃分為多個有序的階段,例如,預設生命週期大致包括:編譯、測試、打包、安裝、部署等。
3. **如何釋出和共享專案**。maven 專案的釋出和共享基於**倉庫**和**座標**兩個基礎,我們可將專案釋出到倉庫,其他人可以通過專案的座標從倉庫中獲取這個專案。
4. **如何處理專案間的關係**。我們可以在 pom.xml 配置對應的座標來依賴其他的專案,而不需要手動地將眾多的 jar 包新增到 classpath 中。
# 下載、安裝
## 專案環境
maven:3.6.3
作業系統:win10
JDK:8u231
## 下載、安裝
進入[官網下載地址](https://maven.apache.org/download.cgi),根據自己的作業系統和 JDK 選擇合適的 maven 版本,這裡我們也可以選擇下載二進位制安裝包或者原始碼包。這裡我選擇版本 3.6.3 的二進位制安裝包。
將下載的 .zip 檔案解壓,可以看到以下的目錄結構:
進行到這一步可以說 maven 已經安裝好了,只是我們還需要進行簡單的配置。
## 環境配置
首先,因為 maven 是由 Java 編寫,需要 JDK 才能執行,所以,我們必須保證配置好了 JAVA_HOME 的環境變數。這個我就不展開了。
然後,將解壓檔案中的 bin 目錄新增到 Path 的環境變數中(我的電腦(右鍵屬性)->高階系統設定->環境變數),如下所示:
![img_maven_environment_variable](https://img2020.cnblogs.com/blog/1731892/202007/1731892-20200722133637978-188291846.png)
## 測試
在任一位置開啟命令列,輸入:`mvn -v`或`mvn --version`,顯示以下內容,說明安裝完成。
![img_maven_mvn_v](https://img2020.cnblogs.com/blog/1731892/202007/1731892-20200722133701551-947328959.png)
# 簡單使用maven
安裝完成後,下面通過一個簡單的例子來模擬構建 maven 專案的過程。
## 生成maven專案
cmd 進入到你想要存放專案的資料夾,輸入以下命令:
```shell
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`,它的檔案結構如下:
```shell
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`目錄,這些資源最終也會被打包到專案根目錄下。
```shell
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
4.0.0
com.mycompany.app
my-app
1.0-SNAPSHOT
my-app
http://www.example.com
UTF-8
1.7
1.7
junit
junit
4.11
test
```
上面生成的專案中包含了兩個類,其中,App.java 有一個列印 Hello World! 的 main 方法。接下來我們嘗試將專案打包並執行。
## 構建專案
maven 將構建和部署專案的過程定義成了很多個有序的`phase`(可以理解為步驟),我們可以執行以下命令來打包專案。
```shell
mvn package
```
這個命令將執行`package`之前的`phase`,如`validate`、`compile`、`test`等,以及執行`package`本身。嚴格上來講,實際上執行的不是`phase`,而是繫結在這些`phase`上的`goal`,後面會展開講解。
執行成功後,專案根目錄生成了一個 target 資料夾,裡面就有打包好的 my-app-1.0-SNAPSHOT.jar。
```shell
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 包了,在命令列輸入
```shell
java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
```
執行完成,可以看到列印:
```shell
Hello World!
```
# maven 的構建生命週期
構建生命週期( build lifecycle )是 maven 的**核心**理論基礎之一,它將專案的構建過程標準化。
maven 有三個**獨立**的生命週期:
1. 預設生命週期。用來定義專案構建的過程。
2. 清理生命週期。用來定義專案清理的過程。
3. 站點生命週期。用來定義專案站點發布的過程。
## 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`所需的引數。
```shell
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`。
```java
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`檔案主要包含以下節點:
```xml
```
這個檔案的配置可以參考[Settings Reference]( http://maven.apache.org/settings.html )。這裡我補充下`servers`、`mirrors`、`profiles`這三個節點的內容。
## servers--配置倉庫認證授權資訊
`servers`用於配置倉庫(包括下載專案和部署專案的倉庫)的認證授權資訊,例如,使用者密碼等。
在具體專案中,我們可以在`pom.xml`中的`repositories`、`pluginRepositories`和`distributionManagement`節點配置用於下載專案和部署專案的倉庫,但是我們不能把認證授權的資訊放在`pom.xml`檔案中,於是`servers`就發揮了作用。
```xml
...
server001
my_login
my_password
${user.home}/.ssh/id_dsa
some_passphrase
664
775
...
```
## Mirrors--配置倉庫的映象
`Mirrors`用於配置下載專案的倉庫映象。前面說過,國內使用 maven 的中央倉庫下載專案比較慢,甚至會出現超時失敗的情況,這時,我們就可以通過配置映象來提高傳輸速度。在此之前,我們需要區分映象和倉庫兩個概念,以下這篇文章作出了很好的解釋。[Maven:mirror和repository 區別](https://www.cnblogs.com/bollen/p/7143551.html)
在下面這個例子中,我們使用阿里雲的映象來請求 maven 的中央倉庫,注意,`mirror`的`mirrorOf`節點必須指定倉庫的 id,當然,這裡還支援多種形式。例如,`* `表示匹配所有遠端倉庫;`repo1,repo2 `表示匹配倉庫 repo1 和 repo2,使用逗號分隔多個遠端倉庫;`*,!repo1` 匹配所有遠端倉庫,repo1 除外,使用感嘆號將倉庫從匹配中排除。
```xml
...
alimaven
aliyun maven
http://maven.aliyun.com/nexus/content/groups/public/
central
...
central
Central Repository
https://repo.maven.apache.org/maven2
default
false
...
```
補充下,`mirror`節點對`repositories`、`pluginRepositories`和`distributionManagement`均生效。
## profiles
`profiles`:提供了一組可選的配置,我們可以根據不同的環境選擇啟用哪一套配置,它包括:`activation`、 `repositories`、`pluginRepositories` 和`properties`四個節點。其中,`activation`節點用於配置該`profile`在什麼環境下才能啟用。
```xml
...
test
false
1.5
Windows XP
Windows
x86
5.1.2600
mavenVersion
2.0.3
${basedir}/file2.properties
${basedir}/file1.properties
${user.home}/our-project
codehausSnapshots
Codehaus Snapshots
false
always
warn
true
never
fail
http://snapshots.maven.codehaus.org/maven2
default
...
...
...
```
**注意,如果`settings.xml`的某個`profile`被啟用,那麼,它的配置將覆蓋`pom.xml`中相同 id 的倉庫以及相同名稱的 property**。
# pom.xml
`pom.xml`幾乎包含了對 maven 專案的所有描述資訊,包括專案的座標、依賴關係、構建配置等等。這個檔案非常重要,官網有這麼一句話,在 maven 的世界裡,一個完整的專案可以不包含任何的程式碼,而只需要一個`pom.xml`。
`pom.xml`的節點如下:
```xml
4.0.0
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
```
關於`pom.xml`檔案的內容,就不詳細展開了,可參考[POM Reference]( http://maven.apache.org/pom.html#pom-reference )。這裡介紹下兩個比較重要的概念。
## scope
`scope`是`dependency`的子節點,用於設定以下兩個內容:
1. 依賴是否在(測試)編譯、(測試)執行等時機加入 classpath。
2. 限制依賴的傳遞性。(假設當前專案為 A,它依賴了 B,如果 C 依賴了 A,則 C 也會依賴 B。可以看出,A 將自己對 B 的依賴傳遞給了 C)
maven 提供了五種`scope`給我們選擇,如下。
| scope | 編譯期 | 執行期 | 測試編譯期 | 測試執行期 | 依賴傳遞 |
| :----------: | :----: | :----: | :--------: | :--------: | :------: |
| **compile** | √ | √ | √ | √ | √ |
| **provided** | √ | √ | √ | √ | × |
| **runtime** | × | √ | × | √ | √ |
| **test** | × | × | √ | √ | × |
| **system** | √ | √ | √ | √ | √ |
通過`dependency`的子節點` optional`可以改變傳遞性。**system對應的依賴不會從倉庫獲取,而是從` systemPath `指定的路徑中獲取**。
## super pom
pom 檔案可以通過``節點來繼承其他專案的配置資訊,而且,和 Java 的物件預設繼承 Object 一樣,pom 檔案預設會去繼承 super pom,該 pom 檔案的內容見: [Super POM for Maven 3.6.3](http://maven.apache.org/ref/3.6.3/maven-model-builder/super-pom.html)
# 常見問題
## 中央倉庫沒有的依賴,怎麼獲取
當我們的專案需要依賴某個在中央倉庫中不存在的依賴,例如,`oracle`的驅動包,我們可以採用三種解決方案:
1. 將依賴的專案安裝到本地倉庫。命令如下:
```shell
mvn install:install-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
```
2. 將依賴的專案安裝到私服。命令如下:
```shell
deploy:deploy-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
```
3. 使用`system`作用域指定包的路徑。
```xml
some.group
non-maven-proj
1.0
system
${java.home}/lib/non-maven-proj.jar
```
# 參考資料
[Apache Maven官網](https://maven.apache.org/)
[Maven:mirror和repository 區別](https://www.cnblogs.com/bollen/p/7143551.html)
> 相關原始碼請移步:[https://github.com/ZhangZiSheng001/maven-demo](https://github.com/ZhangZiSheng001/maven-demo)
>本文為原創文章,轉載請附上原文出處連結:[https://www.cnblogs.com/ZhangZiSheng001/p/13360234.html](https://www.cnblogs.com/ZhangZiSheng001/p/13360234.html)