關於maven的一份小筆記

子月生發表於2020-07-22

簡介

專案裡一直用的 maven,幾乎天天和這個“熟知”的工具打交道,但是,最近我發覺自己對 maven 瞭解的還不夠,例如,什麼是 goal?什麼是 phase?等等。趁著最近有時間,把官網文件大致看了一遍,並且做做筆記,也就形成了這篇部落格。

本文主要講解以下內容:

  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

下載、安裝

進入官網下載地址,根據自己的作業系統和 JDK 選擇合適的 maven 版本,這裡我們也可以選擇下載二進位制安裝包或者原始碼包。這裡我選擇版本 3.6.3 的二進位制安裝包。

img_maven_download

將下載的 .zip 檔案解壓,可以看到以下的目錄結構:

關於maven的一份小筆記

進行到這一步可以說 maven 已經安裝好了,只是我們還需要進行簡單的配置。

環境配置

首先,因為 maven 是由 Java 編寫,需要 JDK 才能執行,所以,我們必須保證配置好了 JAVA_HOME 的環境變數。這個我就不展開了。

然後,將解壓檔案中的 bin 目錄新增到 Path 的環境變數中(我的電腦(右鍵屬性)->高階系統設定->環境變數),如下所示:

img_maven_environment_variable

測試

在任一位置開啟命令列,輸入:mvn -vmvn --version,顯示以下內容,說明安裝完成。

img_maven_mvn_v

簡單使用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,如validatecompiletest等,以及執行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 有三個獨立的生命週期:

  1. 預設生命週期。用來定義專案構建的過程。
  2. 清理生命週期。用來定義專案清理的過程。
  3. 站點生命週期。用來定義專案站點發布的過程。

phases

一個生命週期包括了許多具體的phase(可以理解為步驟),如下:

預設生命週期

一般我們接觸比較多的是預設生命週期,它主要包括以下過程:

  • validate - 校驗專案是一個正確的 maven 專案
  • compile - 編譯程式碼
  • test - 測試src/test/java中的方法,src/test/java的內容僅作為測試使用,不會進行打包或部署
  • package - 將專案打包為可執行的 jar、war 等二進位制軟體包。
  • install - 將軟體包安裝到本地倉庫
  • deploy - 將軟體包部署到遠端倉庫

除了這幾個常用的phase,還有initializegenerate-sourcesprocess-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方法

img_maven_goal01

bindings

和上面這個命令不同,下面的這個命令我們並沒有傳入goal,傳入的是phase

mvn clean

執行這個命令,可以看到,這個命令也是執行了外掛的方法,換句話來講,就是執行了goal

img_maven_phase01

這裡就涉及到一個很重要的概念:當我們在命令中指定了phase,執行的並不是phase本身,而是繫結在phase上面的goal,繫結的goal數量可以是一個也可以是多個

下面是官方給的部分bindingphasegoal的繫結關係主要和專案的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,會出現下面的資訊:

img_maven_phase02

package之前的phase,包括compiletest等都會被執行,而且是有序的。

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。這裡我補充下serversmirrorsprofiles這三個節點的內容。

servers--配置倉庫認證授權資訊

servers用於配置倉庫(包括下載專案和部署專案的倉庫)的認證授權資訊,例如,使用者密碼等。

在具體專案中,我們可以在pom.xml中的repositoriespluginRepositoriesdistributionManagement節點配置用於下載專案和部署專案的倉庫,但是我們不能把認證授權的資訊放在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 的中央倉庫,注意,mirrormirrorOf節點必須指定倉庫的 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節點對repositoriespluginRepositoriesdistributionManagement均生效。

profiles

profiles:提供了一組可選的配置,我們可以根據不同的環境選擇啟用哪一套配置,它包括:activationrepositoriespluginRepositoriesproperties四個節點。其中,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

scopedependency的子節點,用於設定以下兩個內容:

  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 檔案可以通過<parent>節點來繼承其他專案的配置資訊,而且,和 Java 的物件預設繼承 Object 一樣,pom 檔案預設會去繼承 super pom,該 pom 檔案的內容見: Super POM for Maven 3.6.3

常見問題

中央倉庫沒有的依賴,怎麼獲取

當我們的專案需要依賴某個在中央倉庫中不存在的依賴,例如,oracle的驅動包,我們可以採用三種解決方案:

  1. 將依賴的專案安裝到本地倉庫。命令如下:
mvn install:install-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
  1. 將依賴的專案安裝到私服。命令如下:
deploy:deploy-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
  1. 使用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>

參考資料

Apache Maven官網

Maven:mirror和repository 區別

相關原始碼請移步:https://github.com/ZhangZiSheng001/maven-demo

本文為原創文章,轉載請附上原文出處連結:https://www.cnblogs.com/ZhangZiSheng001/p/13360234.html

相關文章