The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

重新認識 Apache Maven 建置與套件管理工具

許多寫 Java 的朋友都採用 Apache Maven 做為專案的建置工具,但是 Apache Maven 有相當多的內涵,我發現並沒有很多人可以很好的解釋這套工具,甚至有一些錯誤的認知,我自己也是在深入瞭解後才發現原來 Apache Maven 可以做這麼多事,甚至你可以拿 Apache Maven 來建置任意程式語言開發的專案。這篇文章我就來好好介紹一下這套優異的建置與套件管理工具。

Apache Maven

這篇文章的介紹內容大多翻譯自 Apache Maven 官網文件,但加入了許多我個人的理解與註釋,若是有理解不正確的地方,也歡迎留言討論。

簡介 Apache Maven

Apache Maven 是一套多功能的軟體專案管理工具,但他並不是一般人講的那種「專案管理」工具,而是一種基於一種 POM (Project Object Model) 的抽象概念,以集中的資訊管理,幫助你管理專案建置流程(Build)、產生專案報表(Reporting)、產生專案文件(Documentation)等工具。其中管理建置流程的部分,自然包含了常見的相依套件管理(Dependencies)、版控系統整合(SCMs)、自動化建置流程(CI)、發行管裡(Releases)、散佈管裡(Distribution)、輔助開發流程等能力。

Maven 這個字來自意第緒語(Yiddish) (是一種以色列的非官方語言,有「知識累積者」的含意。早期是為了簡化 JakartaTurbine 專案的建置流程,當時有好幾個子專案都需維護個別的 Ant 建置檔,但每個都有點不太一樣,而且 JARs 檔都需要 commit 到 CVS 版控中。由於面臨到一些「管理」上的問題,團隊想要打造一個更標準的方法來打造專案,用一種更清楚的方式定義專案的組成,以及想出一套可以在多個專案共用 JARs 檔的方式。

最終的結果,就是他們打造出了一個工具,可以用來建置與管理任何以 Java 為主的專案。這整件事最重要的,就是可以幫助 Java 開發人員可以更好的「理解」他們每天在開發的 Java 專案。這套工具的名稱就叫做 Apache Maven

我個人也是在瞭解了歷史前沿後,才真正看懂 Apache 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.

句子中的 comprehension 就是「理解力」的意思,也就代表著你可以透過 POM 理解整個專案的架構、建置流程,這才是初衷!👍

缺乏理解的專案

前陣子我收到一個客戶提供的一批 Java 原始碼,裡面有好幾個 Java 專案,就很單純的給我一個壓縮檔,沒有使用任何建置工具,大多專案都只有一個 src 資料夾,一堆 *.java 原始碼,然後說 JDK 版本要安裝 1.6 版本,然後就沒有然後了。

你當然可以很簡單的使用 javac 就能編譯所有原始碼,但是我在建置完後還是無法正常執行,搞了好久才發現,原來專案中還有多國語系所需的 messages_*.properties 資源檔,但是這部分客戶完全沒說,也沒有文件,只有一句話:「我們都用 Eclipse 建置的,我們這邊建置都沒有問題」,但這個專案我同事花了快一週才建置成功,真的太難理解了。

為了這個專案,我就嘗試使用 Apache Maven 打造屬於這個專案的建置工具,加入了 pom.xml 檔之後,就等於定義了這個 Java 專案的重要細節,之後我們就可以用更為抽象的方式建置與封裝專案,未來接手此專案的人也會相對輕鬆很多:

mvn package

Maven 的主要目標

Maven 存在的主要目的,是為了「幫助開發者」可以在「短時間內」迅速「理解」開發一個專案所需的各種狀態。為了達成這個目標,Maven 設定了以下目標:

  1. 讓建置過程變的容易 (Making the build process easy)

    如果從結果論上來看,建置專案確實變容易了,他透過一些抽象概念,完美的封裝了建置專案所需的種種細節。不過你也要知道,其實 Maven 有一套相當複雜的建置系統,對一個相對複雜的專案來說,不去認真理解或閱讀 Maven 的文件,其實是相當難以理解的。其實「抽象化」的過程本來就會同時產生正面與反面的影響,全看你從什麼角度來思考。

  2. 提供一致的建置系統 (Providing a uniform build system)

    Maven 在建置專案時,使用了 project object model (POM) 與一組 plugins 外掛套件,只要你熟悉了 Maven 整套機制,未來在面對一個專案或是多個專案時,其實是沒有多大差異的,這將可以節省你大量理解專案的時間。

  3. 提供有品質的專案資訊 (Providing quality project information)

    Maven 可以從你的 POM 檔中擷取許多專案相關資訊,也可以從你的專案原始碼中產生出各種有意義的資訊。例如:

    1. 可直接從原始碼中產生變更記錄 (Change log created directly from source control)

      參見: Apache Maven Changelog Plugin - Introduction

    2. 可產生原始碼檔案或類別之間的參考資訊 (Cross referenced sources)

      參見: Source Code Cross Reference

    3. 有 Plugins 可以幫你管理郵件清單 (Mailing lists managed by the project)

      現在比較少人用 Mailing Lists 來訂閱專案資訊了,但是在開源專案中經常使用。參見: Apache Maven Site Plugin - Project Mailing Lists

    4. 自動整理專案所使用到的所有相依套件 (Dependencies used by the project)

      參見: Maven Surefire Report Plugin

    5. 自動產生單元測試的覆蓋率報告 (Unit test reports including coverage)

      參見: Maven Surefire Report Plugin

    也有許多第三方的程式碼分析產品,可以透過 Maven plugins 的方式加入專案。

  4. 鼓勵更好的開發實踐 (Encouraging better development practices)

    Maven 的宗旨是將一些開發過程中的最佳實踐(Best practices)整併在工具之中,幫助開發人員可以更輕易的做出正確決策。例如:專案規格、執行順序、單元測試報表,這些都是許多公司在建置系統時都會用到的日常工作,這些都已經被內建在 Maven 之中,因此你會透過學習 Maven 的過程知道這些作法。

    目前就單元測試來說,就有以下三種最佳實踐:

    • 將測試程式碼與主要程式碼分開,但擁有相同的目錄結構 (Keeping test source code in a separate, but parallel source tree)
    • 使用常見的命名常規來定義測試案例名稱,幫助你更好找到與執行測試 (Using test case naming conventions to locate and execute tests)
    • 使用測試案例來建立測試環境,取代早期要客製化建置流程的作法 (Having test cases setup their environment instead of customizing the build for test preparation)

    Maven 也可以幫助你完成像是「發行管理」或「議題管理」等常見的專案工作流程。

    Maven 也會透過 archetype 自動幫你建立常見的專案資料夾結構,當你熟悉了這個結構,看見其他透過 Maven 管理的專案也會十分熟悉,降低學習門檻。雖然 Maven 有自己的一套專案目錄結構,但這並無法適用於所有專案類型,但 Maven 還是有保留許多彈性,你一樣可以透過設定調整成你需要的目錄結構。

    如果你的專案真的相當特殊,特殊到使用 Maven 很痛苦的話,那你可能要放棄某些功能,或是乾脆就不要用 Maven 了。

Maven 的主要功能特色

  • 提供最佳實踐的簡單專案範本 - 你可以在幾秒鐘內建立新專案或模組
  • 所有專案都有一致的使用方式 - 意味著新進的開發人員可以大幅縮短上手時間
  • 優秀的相依套件管理,包含自動更新、依賴閉包(dependency closures)或稱傳遞依賴(transitive dependencies)等等
  • 能夠輕鬆地同時處理多個專案
  • 一個龐大且不斷增長的儲存庫,可以幫你快速找到好用的工具與套件,隨裝隨用
  • 擴展性很強,能夠輕鬆地用 Java 或腳本語言開發 plugins
  • 內建最佳設定,大多數情況都可以在無須設定或少量設定的情況下立即使用新功能
  • 在 Maven 之外使用 Ant tasks 來進行相依套件管理與部署工作
  • 基於模型的建置:Maven 能夠將任意數量的專案建置成預先定義好的的輸出類型,例如:JARWAR 或基於專案 metadata 的發行版本,而在大多數情況下無需編寫任何腳本。
  • 專案資訊的一致站點:Maven 能夠使用與建置過程相同的 metadata 自動產生一個網站或 PDF 文件,包括您想要加入的任何文件,並添加到該標準報告中關於專案的開發狀態。這些資訊會顯示在站台左側選單的「專案資訊」和「專案報告」選單中查看
  • 發行管理和散布:無需太多額外配置,Maven 將與您的版控系統 (如 Subversion 或 Git) 進行整合,並根據某個 Tag 管理專案的發布。它還可以將其發佈到分發位置以供其他專案使用。Maven 能夠發佈單獨的輸出,例如 JAR 檔、或是含有其他相依套件與文件的壓縮檔、或是發佈含有原始碼的封裝檔。
  • 相依套件管理:Maven 鼓勵使用 JAR 和其他相依套件的中央儲存庫。Maven 帶有一種機制,專案的用戶端可以使用該機制下載建置專案所需的任何 JAR 檔,這與 Perl 的 CPAN 非常相似。這允許 Maven 的用戶跨專案重用 JAR 檔,並鼓勵專案之間的通訊可以確保更好的處理向後兼容(backward compatibility)等問題。

對 Maven 應建立正確的認知

你可能會聽到有人會這樣描述 Maven:

  • Maven 是一套站台與文件產生器 (Maven is a site and documentation tool)
  • Maven 基於 Ant 並讓你自動下載相依套件 (Maven extends Ant to let you download dependencies)
  • Maven 是一組可重複利用的 Ant 腳本 (Maven is a set of reusable Ant scriptlets)

雖然 Maven 真的也有做這些事,但 Maven 不是只有做這些事,而且這些事跟 Maven 的「主要目標」還是相當不同的,思維上的出發點是完全不同的!👍

快速體驗 Maven 的使用方式

  1. 從專案範本(archetype)建立新專案(artifact)

    Bash

    mvn -B archetype:generate \
      -DgroupId=com.mycompany.app \
      -DartifactId=my-app \
      -DarchetypeArtifactId=maven-archetype-quickstart \
      -DarchetypeVersion=1.4
    

    PowerShell

    mvn -B archetype:generate `
      '-DgroupId=com.mycompany.app' `
      '-DartifactId=my-app' `
      '-DarchetypeArtifactId=maven-archetype-quickstart' `
      '-DarchetypeVersion=1.4'
    

    Command Prompt

    mvn -B archetype:generate ^
      -DgroupId=com.mycompany.app ^
      -DartifactId=my-app ^
      -DarchetypeArtifactId=maven-archetype-quickstart ^
      -DarchetypeVersion=1.4
    
  2. 查看專案目錄結構

    my-app
    |-- pom.xml
    `-- src
        |-- main
        |   `-- java
        |       `-- com
        |           `-- mycompany
        |               `-- app
        |                   `-- App.java
        `-- test
            `-- java
                `-- com
                    `-- mycompany
                        `-- app
                            `-- AppTest.java
    
  3. 查看 my-app/pom.xml 檔案內容

  4. 編譯專案

    mvn compile
    
  5. 執行測試

    mvn test
    

    僅編譯測試程式

    mvn test-compile
    
  6. 封裝應用程式

    mvn package
    

    這個命令會在 target/ 目錄建立一個 *.jar 檔 (my-app-1.0-SNAPSHOT.jar)

  7. 安裝專案到本機的 Maven Repository

    mvn install
    

    這個命令會將 JAR 檔安裝至 ~/.m2/repository/ 底下 (Local Maven Repository)

  8. 產生文件網站

    mvn site
    

    這個命令會自動產生一個靜態網站到 ~/target/site/ 目錄下

    你可以用 Node.js 的 serve 啟動這個文件網站

    npm install --global serve
    
    serve ./target/site
    

    image

  9. 清除 target/ 資料夾

    mvn clean
    

相關連結