1. 程式人生 > >關於maven的一份小筆記

關於maven的一份小筆記

# 簡介 專案裡一直用的 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)