Maven入門知識

目錄

前言

Maven快速入門

pom.xml編寫

編寫項目主代碼

編寫測試代碼

打包和運行

Maven座標和依賴

座標詳解

依賴的基本配置

Maven常用命令總結


前言

之前特地學習過Maven的相關基本知識點,但是當時並未動筆記錄,導致很多基本標籤的含義根本不記得又無處尋找,只能重新翻看書本,非常的繁瑣,特對自己需要的東西做一些簡單的入門總結。

 

Maven快速入門

pom.xml編寫

首先創建一個maven工程,然後我們來看下maven的核心即pom.xml。POM(Project Object Model,項目對象模型)定義了項目的基本信息,用於描述項目如何被構建,聲明項目依賴等等。如下是我剛新建的hello-world項目的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.qingcha.maven</groupId>
    <artifactId>hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>


</project>

代碼的第一行是XML頭,指定了該xml文檔的版本和編碼方式。緊接着是project元素,project是所有pom.xml的根元素,它還聲明瞭一些pom相關的命名空間及xsd元素,雖然這些屬性並不是必須的,但使用這些屬性能夠讓第三方工具(如ide中的xml編輯器)幫助我們快速的編輯pom。

根元素下的第一個子元素modelVersion指定了當前pom模型的版本,對於Maven 2 及 Maven 3 來說,它只能是4.0.0。

最重要的是接下來的三個子元素groupId、artifactId、version,他們共同定義了一個項目基本的座標,在Maven的世界裏,任何的jar、pom或者war都是以基於這些基本的座標來進行區分的。

groupId定義了項目屬於哪個組,這個組往往和項目所在的組織或公司存在關聯。譬如在googlecode上建立了一個名爲myapp的項目,那麼groupId就應該是com.googlecode.myapp。

artifactId定義了當前Maven項目在組中唯一的ID,組中不同的子項目(模塊)往往分配不同的唯一標識。

version指定了當前項目當前的版本---1.0-SNAPSHOT,SNAPSHOT意爲快照,說明該項目還處於開發中,是不穩定的版本。隨着項目的發展,version會不斷更新,如升級爲1.0、1.1-SNAPSHOT、1.1等。

編寫項目主代碼

項目主代碼和測試代碼不同,主代碼會被打包到最終的構件中(如jar),而測試代碼只在測試運行時用到,不會被打包。默認情況下,Maven假設項目主代碼位於 src/main/java 目錄,只要遵循該約定,無需額外的配置,Maven會自動搜尋該目錄找到項目主代碼,因此我們需要遵循Maven的約定在該目錄下創建我們自己的文件:com/qingcha/maven/helloworld/HelloWorld.java,其內容如下:

package com.qingcha.maven.helloworld;

public class HelloWorld {

    public String sayHello() {
        return "hello, world";
    }

    public static void main(String[] args) {
        System.out.println(new HelloWorld().sayHello());
    }
}

這裏需要注意的一點是,項目中Java類的包都應該基於項目的groupId和artifactId,這樣更加清晰,更加符合邏輯,也方便搜索構件或者Java類。

代碼編寫完畢後,使用maven進行編譯,在項目根目錄下運行命令:mvn clean compile,然後會看到控制檯輸出如下信息

[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.qingcha.maven:hello-world >--------------------
[INFO] Building hello-world 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO] Deleting /project/Open-source/helloworld/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /project/Open-source/helloworld/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.050 s
[INFO] Finished at: 2020-01-04T12:46:11+08:00
[INFO] ------------------------------------------------------------------------

從輸出信息來看,maven主要做了三個操作:

  • clean任務:運用clean插件首先刪除target/目錄,默認情況下maven構建的所有輸出都在target/目錄中
  • resources任務:由於我們未定義項目資源,此處略過
  • compile任務:將項目主代碼編譯至target/classes目錄下

編寫測試代碼

爲了使項目結構清晰,項目主代碼與測試代碼應該分別位於獨立的目錄中,上文提到主代碼的目錄爲 src/main/java ,對應的Maven

項目中默認的測試代碼目錄是 src/test/java ,因此我們也需要遵循約定在該目錄下創建我們的測試類。當然爲了測試首先還需要引入依賴,在java中一般都是使用junit項目,依賴如下

<?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.qingcha.maven</groupId>
    <artifactId>hello-world</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

上述xml中添加了dependencies元素,該元素下可以包含多個dependency元素以聲明項目的依賴,這裏添加了junit的依賴,由前面知識我們已經知道groupId、artifactId、version三者構成了一個Maven項目最基本的座標,有了這段聲明,Maven就能夠自動下載junit-4.12.jar(從Maven的中央倉庫進行下載)。

後面還有一個scope元素,scope爲依賴範圍,若依賴範圍爲test,則表示該依賴只對測試有效。換句話說,在測試代碼中使用junit代碼是沒有問題的,但是在主代碼中使用junit代碼就會造成編譯錯誤。如果不聲明該元素,默認的範圍爲compile,表示該依賴對主代碼和測試代碼都有效。下文會詳細介紹

接下來就是創建測試類了,在mac版idea下,將鼠標放至要創建測試類的類名下,然後按 option+enter 可快速創建測試類,測試類簡單代碼如下:

package com.qingcha.maven.helloworld;

import org.junit.Assert;
import org.junit.Test;

public class HelloWorldTest {

    @Test
    public void sayHello() {
        HelloWorld helloWorld = new HelloWorld();
        Assert.assertEquals("hello, world", helloWorld.sayHello());
    }
}

然後我們使用Maven執行測試,運行 mvn clean test ,輸出信息如下:

[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.qingcha.maven:hello-world >--------------------
[INFO] Building hello-world 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO] Deleting /project/Open-source/helloworld/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /project/Open-source/helloworld/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /project/Open-source/helloworld/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /project/Open-source/helloworld/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /project/Open-source/helloworld/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.qingcha.maven.helloworld.HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.069 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.017 s
[INFO] Finished at: 2020-01-04T13:10:58+08:00
[INFO] ------------------------------------------------------------------------

由最下方的success表明我們的測試類執行通過了,分析上面的輸出信息,Maven一共執行了六個任務:

  • clean任務:運用clean插件首先刪除target/目錄,默認情況下maven構建的所有輸出都在target/目錄中
  • resources任務:由於我們未定義項目資源,此處略過
  • compile任務:將項目主代碼編譯至target/classes目錄下
  • testResources任務:資源未定義,略過
  • testCompile任務:測試代碼編譯
  • test任務:執行測試

打包和運行

將項目進行編譯、測試之後,下一個重要步驟就是打包(package),當前新建的項目並沒有指定打包類型,使用默認打包類型jar,簡單的執行命令 mvn clean package 進行打包,可以看到輸出

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-world ---
[INFO] Building jar: /project/Open-source/helloworld/target/hello-world-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.010 s
[INFO] Finished at: 2020-01-04T13:41:38+08:00
[INFO] ------------------------------------------------------------------------

類似的,Maven會在打包之前執行編譯、測試等操作,然後在最後執行jar任務即使用jar插件將項目主代碼生成一個名爲hello-world-1.0-SNAPSHOT.jar的包,該包同樣位於/target 目錄下。

至此,我們得到了項目的輸出,如果有需要的話就可以複製這個jar文件到其他項目的classpath中從而使用HelloWorld類。但是如何才能讓其他的Maven項目直接引用這個jar呢?還需要一個安裝的步驟,執行 mvn clean install :

[INFO] --- maven-install-plugin:2.4:install (default-install) @ hello-world ---
[INFO] Installing /project/Open-source/helloworld/target/hello-world-1.0-SNAPSHOT.jar to /Users/xujia/Downloads/mydocument/maven/maven-repository/com/qingcha/maven/hello-world/1.0-SNAPSHOT/hello-world-1.0-SNAPSHOT.jar
[INFO] Installing /project/Open-source/helloworld/pom.xml to /Users/xujia/Downloads/mydocument/maven/maven-repository/com/qingcha/maven/hello-world/1.0-SNAPSHOT/hello-world-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.009 s
[INFO] Finished at: 2020-01-04T13:48:19+08:00
[INFO] ------------------------------------------------------------------------

同樣的,執行安裝命令時也會自動執行編譯、測試、打包任務,從輸出信息可以看到install任務將項目輸出的jar包安裝到了Maven本地倉庫中,你可以打開相應的文件夾看到HelloWorld項目的pom和jar。之前講述junit的pom及jar的下載的時候,我們說過只有構件被下載到本地倉庫後,才能由所有Maven項目使用,這裏是同樣的道理,只有將HelloWorld的構件安裝到本地倉庫後,其他Maven項目才能使用它。

默認打包生成的jar包是不能夠直接運行的,因爲帶有main方法的類信息不會添加到manifest中(打開jar文件中的META-INF/MANIFEST.MF文件,將無法看到Main-Class一行),爲了生成可執行的jar文件,我們需要藉助maven-shade-plugin插件(有多種方式均可以生成可執行的jar包,有興趣可自行google),配置該插件如下:

<build>
                    <!-- 兩種方式可以生成可執行jar包 -->
            <!--<plugin>-->
                <!--<groupId>org.apache.maven.plugins</groupId>-->
                <!--<artifactId>maven-shade-plugin</artifactId>-->
                <!--<version>3.2.1</version>-->
                <!--<executions>-->
                    <!--<execution>-->
                        <!--<phase>package</phase>-->
                        <!--<goals>-->
                            <!--<goal>shade</goal>-->
                        <!--</goals>-->
                        <!--<configuration>-->
                            <!--<transformers>-->
                                <!--<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">-->
                                    <!--<mainClass>com.qingcha.maven.helloworld.HelloWorld</mainClass>-->
                                <!--</transformer>-->
                            <!--</transformers>-->
                        <!--</configuration>-->
                    <!--</execution>-->
                <!--</executions>-->
            <!--</plugin>-->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.qingcha.maven.helloworld.HelloWorld</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
    </build>

此時再運行 mvn clean install ,在/target 目錄下將會看到 hello-world-1.0-SNAPSHOT.jar 和 original-hello-world-1.0-SNAPSHOT.jar,前者是帶有Main-Class信息的可運行jar,後者是原始的jar,打開 hello-world-1.0-SNAPSHOT.jar 的 META-INF/MANIFEST.MF,可以看到它其中便包含了Main-Class信息,便能確定程序的入口在哪裏(解壓jar包的命令爲:jar xvf hello-world-1.0-SNAPSHOT.jar)

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: xujia
Created-By: Apache Maven 3.5.4
Build-Jdk: 1.8.0_172
Main-Class: com.qingcha.maven.helloworld.HelloWorld

最後運行該jar包:java -jar hello-world-1.0-SNAPSHOT.jar,可以看到控制檯輸出hello, world,完美~

 

Maven座標和依賴

座標詳解

Maven座標爲各種構件引入了秩序,任何一個構件都必須明確定義自己的座標,而一組Maven座標是通過一些元素定義的,它們是groupId、artifactId、version、packaging、classifier,先給出一組座標定義,如下:

<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>

下面詳細解釋一下各個座標元素

  • groupId:定義當前Maven項目隸屬的實際項目。首先,Maven項目和實際項目不一定是一對一的關係。比如springframework這一實際項目,其對於的Maven項目會有很多,如spring-core、spring-context等,因此一個實際項目往往會被劃分成很多個模塊,就類似於我們上面說的組的概念。其次,groupId不應該對應項目隸屬的組織或公司,因爲一個組織下會有很多實際項目,如果groupId只定義到組織級別,那麼artifactId只能定義Maven項目,那麼實際項目這個層將難以定義。最後,groupId的表示方式與Java包名的表示方式類似,通常與域名反向一一對應。上例中,groupId 爲 org.sonatype.nexus,org.sonatype表示Sonatype公司建立的一個非盈利性組織,nexus標識Nexus這一實際項目,該groupId與域名 nexus.sonatype.org 對應。
  • artifactId:該元素定義實際項目中的一個Maven項目(模塊),推薦的做法是使用實際項目名稱作爲 artifactId 的前綴,如上例中的nexus-indexer。
  • version:該元素定義Maven項目當前所處的版本,如上例中 nexus-indexer 的版本是2.0.0。
  • packaging:該元素定義Maven項目的打包方式。首先,打包方式通常與所生成構件的文件擴展名對應,如上例中 packaging 爲 jar,則最終的文件名爲 nexus-indexer-2.0.0.jar。而使用war打包方式的Maven項目,最終生成的構件中會有一個 .war 文件,不過這不是絕對的。其次,打包方式會影響到構建的生命週期,比如jar打包和war打包會使用不同的命令。最後,當不定義 packaging 的時候,Maven會使用值 jar
  • classifier:該元素用來幫助定義構建輸出的一些附屬構件。附屬構件與主構件對應,如上例中的主構件是 nexus-indexer-2.0.0.jar,該項目可能還會通過使用一些插件生成 nexus-indexer-2.0.0-javadoc.jar、 nexus-indexer-2.0.0-sources.jar 這樣一些附屬構件,其包含了java文檔和源代碼,這時候,javadoc和sources就是這兩個附屬構件的 classifier。這樣,附屬構件也就擁有了自己唯一的座標。注意,不能直接定義項目的classifier,因爲附屬構件不是項目直接默認生成的,而是由附加的插件幫助生成。

上述5個元素中,groupId、artifactId、version是必須定義的,packaging 是可選的(默認爲jar),classifier 是不能直接定義的。

依賴的基本配置

一個依賴聲明可以包含如下的一些元素:

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
                ...
                <exclusion>
                    ...
                </exclusion>
            </exclusions>
        </dependency>
        ...
    </dependencies>
    ...
</project>

根元素project下的 dependencies 可以包含一個或者多個 dependency 元素,以聲明一個或多個項目依賴,每個依賴可以包含的元素有:

1、groupId、artifactId和version:依賴的基本座標,對於任何一個依賴來說,基本座標是最重要的,Maven根據座標才能找到所需要的依賴

2、type:依賴的類型,對應於項目座標定義的 packaging。大部分情況下,該元素不必聲明,其默認值爲jar。

3、scope:依賴的範圍,比較常用的有三種:

  • compile:編譯依賴範圍,如果沒有指定,就會默認使用該依賴範圍。使用此依賴範圍的Maven依賴,對於編譯、測試、運行三種classpath都有效
  • test:測試依賴範圍,只對於測試classpath有效,在編譯主代碼或者運行項目的時候無法使用此類依賴
  • provided:已提供依賴範圍,使用此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在運行時無效。典型的例子是servlet-api,編譯和測試項目的時候需要該依賴,但在運行項目的時候,由於容器已經提供,就不需要Maven重複的引入一遍
  • runtime:運行時依賴範圍,對於測試和運行時有效,編譯時無效

4、optional:標記依賴是否可選,爲true的時候表示是可選依賴,如果項目A依賴於項目B,項目B依賴於項目X和Y,B對於X和Y的依賴都是可選依賴,那麼X和Y將對A沒有任何影響,即不會進行依賴傳遞。

5、exclusions:用來排除傳遞性依賴,需要注意的是,聲明exclusion的時候只需要groupId和artifactId,而不需要version元素,這是因爲只需要groupId和artifactId就能唯一定位依賴圖中的某個依賴。

依賴這一塊東西很多,對於一個好的項目來說,管理好多個項目之間的依賴也很重要,入門篇就先記錄這麼點把。

 

Maven常用命令總結

  • mvn clean compile:刪除/target目錄並編譯項目主代碼
  • mvn clean test:執行全部測試代碼
  • mvn clean package -D maven.test.skip=true:打包,但是不執行測試用例,也不編譯測試用例
  • mvn clean package -U -D skipTests:打包並更新快照,不執行測試用例,但編譯測試用例類生成相應的class文件至target/test-classes下。
  • mvn clean install:將打完的包部署到本地maven倉庫,以供其他項目使用
  • mvn clean deploy:將打完的包部署到本地maven倉庫和遠程maven私服倉庫
  • mvn dependency:tree:查看依賴樹
  • mvn dependency:tree -D output=*.txt:查看依賴樹並將樹輸出到文本中

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章