你竟然沒用 Maven 構建專案?

沉默王二發表於2019-07-03

一年前,當我和小夥伴小龍一起做一個外包專案的時候,受到了嚴重的鄙視。我那時候還不知道 Maven,所以搭建專案用的還是最原始的方式,小龍不得已在匯入專案的時候花了很長時間去下載專案依賴的開源類庫。

出於對我的尊重,小龍沒有破口大罵,而是非常委婉地說了一句:“二哥,你好歹也有一定的知名度了,竟然沒用 Maven 構建專案,真讓我大開眼界啊。”

作為一名富有上進心的程式設計師,不能忍啊。藉此機會,有必要隆重地向大家介紹一下 Maven 了。

01、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.

大致的意思就是說,Apache Maven 是一個專案管理和自動化構建工具,基於專案物件模型(POM)的概念,可以管理專案的構建、報告以及文件。作為 Apache 組織中的一個頗為成功的開源專案,Maven 主要服務於基於 Java 的專案構建、依賴管理和專案資訊管理

Maven 採用了約定優先配置的原則,這些原則如下表所示:

目錄目的
${basedir} 存放 pom.xml 和所有的子目錄
${basedir}/src/main/java 存放 Java 原始碼
${basedir}/src/main/resources 存放專案的資原始檔,比如說 log4j.properties
${basedir}/src/test/java 存放測試類
${basedir}/src/test/resources 存放測試用的資源
${basedir}/src/main/webapp 存放 Web 前端檔案
${basedir}/target 專案打包後的輸出目錄
${basedir}/target/classes 專案編譯後輸出目錄
~/.m2/repository 預設的 Maven 倉庫目錄(~表示使用者目錄)

使用約定優先配置帶來的最大好處就是專案的目錄結構圖非常的統一,不同的開發者在開發一個 Maven 專案的時候,檔案存放位置幾乎沒有差別,省去了很多不必要的麻煩,有利於促進專案團隊的標準化。

我們這個年代非常崇尚開源精神,幾乎所有的 Java 專案都會借用一些第三方的開源類庫,這些類庫可以通過依賴的方式引入到專案中來。但隨著依賴的增多,版本衝突、依賴臃腫的問題就會接踵而來。手工解決這些問題是十分枯燥的,幸運的是 Maven 提供了一個優秀的解決方案,它通過一個三維的座標(<groupId><artifactId><version>)來準確地定位每一個開源類庫。

另外,我認為 Maven 特別優秀的一點是,它把專案依賴的所有開源類庫都從遠端中央倉庫下載到了指定的本地倉庫中,也就是說,這些開源類庫可以在多個專案之間共用,無需重複下載——假如我用 Maven 構建專案的話,小龍就不用下載那麼多開源類庫了,他的本地倉庫中可能已經有了——我也就不會被鄙視了。

02、配置 Maven 環境

1)安裝 Maven 之前,先確保電腦上已經安裝了 JDK。

2)去 Maven 官網下載想要的版本並解壓,下載地址為:

https://maven.apache.org/download.cgi

3)設定環境變數,主要是 MAVEN_HOME 和 Path。

4)開啟命令列,輸入 mvn -v 驗證 Maven 是否配置成功。

5)Maven 的 conf 目錄下包含了一個非常重要的檔案 settings.xml,一般情況下,我傾向於將該檔案複製到 ~/.m2/ 目錄下——建議你也這麼做。

開啟該檔案,在 <mirrors></mirrors> 節點下新增阿里雲的映象地址(參照下面的程式碼)。為什麼要這麼做呢?因為 Maven 預設的倉庫在不翻強的情況下很難訪問到。

<mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>        
</mirror>

這裡需要注意的是,<mirrorOf> 的值有下面四種方式:

  • *:匹配所有遠端倉庫,也就是說任何對於中央倉庫的請求都會轉至該映象。
  • external:*:匹配所有不在本機上的遠端倉庫。
  • repo1,repo2:匹配倉庫 repo1 和 repo2,使用逗號分隔多個遠端倉庫。
  • *,!repo:匹配所有遠端倉庫,repo 除外,使用感嘆號將倉庫從匹配中排除。

6)在 Eclipse 下依次選擇選單 Window → Show View → Other → Maven → Maven Workspace Build,檢視映象是否配置成功(我習慣了使用 Eclipse,所以請見諒,不過不管使用哪種整合開發環境,思路和步驟都大差不差)。

03、快速建立 Maven 專案

為了儘快步入重點,這一小節我會有意的避重就輕,沒必要的步驟會略過。

1)在 Eclipse 中新建專案的時候選擇 Maven Project。

2)在接下來選擇專案型別的時候,選擇 maven-archetype-quickstart,如下圖所示。

3)然後指定專案引數的時候填寫 group id 和 artifact id。

4)專案建立成功後的目錄結構圖如下圖所示。

Maven 3 預設使用的依然是 JDK 1.5,不過我們可以為其配置更高版本的 JDK,後面會聊到。

04、詳細分析 pom.xml

毫無疑問,Maven 專案的靈魂只有一個,那就是 pom.xml 檔案,所以接下來我會詳細地對其進行分析。

1)專案基本資訊

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>

  <groupId>com.cmower</groupId>
  <artifactId>test</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>test</name>

</project>

①、<project> 是 pom.xml 的根元素,宣告瞭相關的名稱空間。

②、<modelVersion> 指定了當前專案物件模型(POM)的版本,對於 Maven 3.x 來說,該值只能是 4.0.0。

③、<groupId> 定義了專案的組名,這個組往往和專案所在的組織或公司關聯。

④、<artifactId> 定義了當前 Maven 專案在組中唯一的 ID。

⑤、<version> 定義了專案的版本號,SNAPSHOT 為快照的意思,也就是說該專案還處於開發階段。

⑥、<packaging> 定義了專案的打包型別,可選值有 war、jar 等。

⑦、<name> 定義了專案的名稱。

2)變數配置資訊

pom.xml 檔案的第二部分通常用來配置一些變數資訊。

<properties>
    <spring.version>5.1.5.RELEASE</spring.version>
</properties>

有了變數的配置資訊後,可以通過 ${spring.version} 的形式來呼叫這些配置項。這樣做的好處顯而易見,當依賴項的版本升級的時候,可以直接修改變數值即可。

3)依賴管理

阿里雲的 Maven 倉庫下有各種各樣的第三方類庫,換句話說就是,只有你想不到的,沒有你找不到的。大多數 Maven 專案的依賴項列表都會很長很長,為了便於說明,下面我只列出某些具有特色的。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.1</version>
    </dependency>
</dependencies>

①、上文中曾提到,<groupId><artifactId><version> 合起來可以準確地定位一個依賴項。那怎麼找到想要的依賴項呢?

第一步,進入 MavenRepository 網站,地址如下:

http://mvnrepository.com

然後在搜尋框中輸入第三方類庫的關鍵字,比如說「spring-core」,點選「search」按鈕,可以檢視到該類庫的連結導航。

第二步,點選連結進入到「spring-core」的主頁,可以看到所有版本的「spring-core」,選擇一個使用率最高的。使用率高在一定程度上表明這個版本的類庫最穩定,它已經得到了廣大程式設計師的認可。

第三步,進入該版本的主頁,只需要左鍵輕輕地在 「Maven」選項卡內點一下,就已經把該類庫的 Maven 依賴資訊複製到貼上板了(不需要「Ctrl+C」,非常的人性化),如下圖所示。

第四步,將類庫的依賴資訊貼上到 pom.xml 檔案的 <dependencies> 節點下,然後按下快捷鍵「Ctrl+S」儲存。緊接著,依次展開 test → Java Resources → Libraries → Maven Dependencies 節點,你可以看到該類庫已經悄悄地新增進來了。

②、 <exclusions> 主要用於排除依賴。

有時候,我們引入的依賴中可能會包含一些不想要的依賴包,我們想引入自己想要的,這時候就要用到排除依賴了。

使用 <exclusion> 的時候只需要指定 groupId 和 artifactId 就行了,並不需要 version,這是因為 groupId 和 artifactId 就可以定位某種型別的依賴。

③、 <scope> 用來控制依賴的範圍。

test:測試依賴範圍。典型的例子是 Jnuit,它只有在編譯測試程式碼及執行測試的時候才需要。

compile:編譯依賴範圍(其實不止是編譯,對測試、執行同樣有效),預設項,如果沒有指定,就會預設使用該依賴範圍。

provided:提供依賴範圍。對編譯和測試有效,但在執行時候無效。

runtime:執行時依賴範圍。對測試和執行有效,但在編譯時無效。

PS:如果不知道選哪一種,預設就對了。

4)構建配置

<build> 元素中包含了執行 Maven 構建週期目標所需的外掛以及相關的配置。

<build>
       <finalName>test</finalName>
       <plugins>

           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>2.3.2</version>
               <configuration>
                   <source>${jdk.version}</source>
                   <target>${jdk.version}</target>
               </configuration>
           </plugin>

       </plugins>

       <resources>
           <resource>
               <directory>src/main/java</directory>
               <includes>
                   <include>**/*.xml</include>
               </includes>
           </resource>
       </resources>
</build>

①、<finalName>,打成 jar 包或者 war 包時的名稱。上文中曾提到,專案打包後的輸出目錄為 ${basedir}/target

②、<plugins> 用於指定專案在構建過程中使用的外掛。

  • 用於確定使用的外掛。
  • ,該外掛所需要的特殊配置。Maven 3 預設使用的是 JDK 1.5,本例中我們使用了 JDK 1.8。

③、<resources> 描述了各個資源在 Maven 專案中的具體路徑。

  • ,資原始檔的路徑,預設位於 ${basedir}/src/main/resources/ 目錄下。
  • ,用於指定在構建過程中被處理的資原始檔;對應 用於省去不被處理的資原始檔。

05、使用 Maven 對專案進行清理、編譯、測試、打包

1)清理:mvn clean,該命令會刪除 target 目錄。可以直接在命令列中執行該命令,只需要切換到專案所在的路徑下即可。

2)編譯:mvn complie,該命令會編譯 src/main/java 目錄下的原始碼。同時,Maven 還會處理在 src/main/resources 目錄下的資原始檔,確保它們作為編譯的一部分。

不過,很遺憾的是,執行該命令會報錯。該命令給出的提示是,檢視 [Help 1] 給出的地址,我嘗試了一下,可以將 mvn complie 命令替換為 mvn compiler:compile 命令執行,結果如下圖所示。

編譯後可以在 target 目錄下檢視位元組碼檔案。

3)測試:mvn test,test 命令在執行時,會執行 compile 命令;而之前我們已經執行過一次 compile 命令,為了確保結果的準確性,可以執行 mvn clean test 命令確保測試之前沒有殘餘物,結果如下圖所示。

Maven 會通過 Surefire 外掛,使用 pom.xml 檔案中的測試提供者(通常是 Junit)執行測試。執行 test 命令不僅會執行測試,還會產生報告檔案,此時 target 目錄下的截圖如下:

4)打包:mvn install,該命令會按照 pom.xml 檔案中 <packaging> 指定的方式(本例為 jar)對編譯結果打包。同時,還會把打包好的檔案放到本地的 Maven 倉庫中,以便其他專案把它當做依賴項使用。命令執行結果如下圖所示。

檢視本地的 Maven 倉庫,可以看到剛剛打包好的檔案。

06、最後

在 Maven 出現之前,流行的構建工具是 Ant;在 Maven 出現之後,還有一種新興的構建工具 Gradle,它有意選擇了和 Maven 相反的原則,不會強制使用者遵循刻板的構建週期。

總之,Maven 是一款優秀的構建工具,Java 專案的開發者很有必要熟練地掌握它。

相關文章