關於maven的一份小筆記

簡介

項目里一直用的 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 是由 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="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //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>//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="//maven.apache.org/SETTINGS/1.0.0"
      xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="//maven.apache.org/SETTINGS/1.0.0
                          //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="//maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="//maven.apache.org/SETTINGS/1.0.0
                      //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="//maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="//maven.apache.org/SETTINGS/1.0.0
                      //maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <mirrors>
  		<mirror>
			<id>alimaven</id>
			<name>aliyun maven</name>
			<url>//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>//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="//maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="//maven.apache.org/SETTINGS/1.0.0
                      //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>//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="//maven.apache.org/POM/4.0.0"
  xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="//maven.apache.org/POM/4.0.0
                      //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 區別

相關源碼請移步://github.com/ZhangZiSheng001/maven-demo

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