如何將私有的 JAR 檔加入到由 Apache Maven 管理的 Java 專案中
我們最近有個新的 Java 專案,客戶提供原始碼之後,卻發現他們自己架設的 Maven Repository 並沒有開啟防火牆讓我們連線,以致於專案無法下載相依套件而無法建置。但除了開防火牆連線外,其實還有很多方法可以讓你獲取 Maven 所需的相依套件。今天這篇文章,我就來分享關於 Maven 如何管裡相依套件,以及如何正確的將私有的第三方 JAR 檔加到專案內,降低團隊取得這些 JAR 檔的門檻。
其實我們已經不是只有一次需要將第三方的 JAR 檔(3rd-party JARs)加入到 Maven 的相依套件中,但是這種想法其實跟 Apache Maven 希望達到的目的有一點點衝突。
如果直接使用 IntelliJ IDEA、Eclipse 或 VSCode 內建的 Build system 來建置專案,要加入一個 JAR 檔到專案中其實非常容易。然而就算你不用任何 Build tools 也可以在透過 javac
編譯 Java 程式時加入 -classpath
參數,直接參考特定的 JAR 檔也很基本。但是當你用了 Apache Maven 來幫你管裡相依套件後,它把 Project
與 JAR file
之間做了一層抽象,透過一種所謂 Dependency
(相依套件) 的概念來串聯兩者之間,你必須理解這個概念,才能很清楚的知道如何更輕易的管裡相依關係。
當你在使用 Apache Maven 的時候,它有一套相當完整的 Dependency Mechanism (相依管裡機制),今天這篇文章不打算深入相依管理的細節,有興趣請自行 閱讀文件 。
這篇文章的主軸,是從一個很簡單的需求出發: 我有一個私有的 JAR 檔,希望加入一個由 Apache Maven 管理的 Java 專案中!
因此,你必須先瞭解何謂 Repositories
或 Artifact Repositories
!
理解 Artifact Repositories 的概念
在瞭解 Artifact Repositories 之前,我們先來認識幾個抽象的概念與專有名詞。
我在第一次接觸 Apache Maven 的時候,就是學寫 Spring Boot 的時候,透過 Spring Initializr 建立新專案的時候一定要輸入 groupId
、 artifactId
這兩個參數,我問了好幾個人,沒有人可以把這兩個概念解釋的很清楚,就只知道要設定這兩個「參數」而已。這種 知其然而不知所以然 的狀態,會讓我十分困擾。因為我學習任何新事物都一定要「連結」某個既有概念,即便當下對觀念的理解不夠全面,還是要先連結起來輔助我思考,待日後有更深刻的理解時,還可以逐步導正觀念。
以下我就來說說這幾個重要的參數所代表的意義:
groupId
這裡的
group
有 群組 的意思,也可以理解為一個 團體 ,更可以理解為一個 組織 。而每個 專案 都會隸屬於一個 組織 ,或是 一個組織 可以包含 多個專案 ,這是個「一對多」關係,我想這部分非常容易理解。一般來說這個
groupId
會取名成一個組織的 FQDN 域名,例如我們公司的網址是duotify.com
,所以公司層級的專案,就會取名為com.duotify
當成專案的groupId
。如果是公司內部isms
團隊的專案,我們的groupId
就會取名為com.duotify.isms
,以此類推。我們再以
org.apache.maven.plugins
這個groupId
為例,這個名字所代表的含意是:所有使用這個groupId
的專案,全部都歸類在org.apache
組織下的maven
計畫中的所有plugins
專案」artifactId
這裡的
artifact
有 產出物 的意思,也有 人工製品 的意思,其代表的真實含意是 手工打造的物品 。這個抽象的概念,其實就是我在文章中經常提到的 專案 (Project)。你所親手打造的 專案 ,最終的 產出物 就是Artifact
。每個 專案 都會有個名稱,而這個名稱就叫做
artifactId
!以 Java 專案而言,無論你是一個 Class Library 或是 Spring Boot 專案,通常專案的最終 產出物 通常是一個 JAR 檔 (
*.jar
) 或 WAR 檔 (*.war
)。然而,一個專案可能會產生不只一個產出物,因此你還有可能產出含有原始碼的 JAR 檔,其檔名通常也會包含artifactId
所標示的名稱。version
我們在一個 組織 (
groupId
)下,可能會發展多個 專案 (artifactId
),而每個專案可能會產出多個 版本 (version
),因此groupId
+artifactId
+version
就會自然組成一個全世界唯一的 套件名稱 。通常一個由 Maven 產出的 Artifact 的檔名通常是
<artifactId>-<version>.<extension>
這種格式,例如:myapp-1.0.jar
如果我們把焦點拉回這個段落的主軸,什麼是 Artifact Repositories 呢?就是一個可以用來儲存 Artifact
(專案產出物) 的儲存庫。
理解 Artifact 與 Project 之間的關係
我們現在知道 Artifact
就是 Project
的產出物,因此我們開發 Spring Boot 的時候,最終產生的 JAR 檔,就是該 Project
的 Artifact
而已。
我們在一個 Spring Boot 專案中所使用的 Dependency
(相依套件),說穿了,也就是你在使用其他 Project
所產生的 Artifact
(產出物) 而已。
理解了這個概念之後,你就要重新思考一遍,什麼叫做 Dependency
(相依套件):
所謂 Dependency
就是你 需要 使用到 另一個專案 的 產出物 來幫助你在這個專案完成任務,但你不用管那個專案的實作細節,也不用管那個專案如何封裝產出物,你只需要很簡單的知道對方的 groupId
+ artifactId
+ version
就可以取用其結果,而這就是「抽象化」的過程,你必須理解這個過程,這整件事才不會是「抽象」的,也才能舉一反三、靈活運用。
重新理解 Artifact Repositories 的細節
我們現在知道 Artifact Repositories 就是一個可以用來儲存 Artifact
(專案產出物) 的儲存庫,跟 Git Repository 一樣,他也是以檔案的形式來儲存這些內容,他其實就是一個資料庫,除了用來儲存產出物之外,還會保存一些重要的 Metadata,幫助 Maven 取得關鍵資訊。
從 Introduction to Repositories 文件中有提到一段話:
他說在 Maven 之中,一個「儲存庫」會用來保存不同類型的 build artifacts
(建置成果) 與 dependencies
(相依套件),而且主要有兩種類型:
local
(本地儲存庫)儲存在本機的 Maven Repository,通常位於
~/.m2/
目錄下,你可以把他視為是一種 遠端儲存庫 的 快取版本 ,因為所有從 遠端儲存庫 下載的artifacts
最終都會儲存在 本地儲存庫 之中,也就是存在~/.m2/
目錄下。remote
(遠端儲存庫)有別於 本地儲存庫 ,所謂的 遠端儲存庫 其實是一個「相對」的概念,他就是一個儲存在「遠端」的儲存庫而已,任何使用
file://
或https://
開頭的協定,都可以被歸類在「遠端儲存庫」這個類別。所以你可以把所謂的 遠端儲存庫 部署在任何以 HTTP / HTTPS 為主的任意網站上,可以在網際網路上,也可以部署在公司內網。你甚至可以不需要架設 Web 伺服器,直接用 NAS 就可以架設一套公司內部用的 遠端儲存庫 來用,只要使用file://
為網址就行。
基本上 local
(本地儲存庫) 與 remote
(遠端儲存庫) 保存 Artifacts
的目錄結構與格式是完全相同的,大家也可以自由進入 ~/.m2/
資料夾查看有哪些檔案,其實都是非常公開透明的。
將私有的 JAR 檔加入到 Maven 本地儲存庫
我們假設一下這個情境,我們有個 C:\Program Files\Java\jdk1.6.0_43\jre\lib\plugin.jar
第三方 JAR 檔,由於你的 Java 專案需要在編譯的時候用到該 JAR 檔中的某些類別,你並沒有該檔案的原始碼,但可以理解這個 plugin.jar
其實就是一個 artifact
(產出物),而且編譯的時候只要將該檔案加入 -classpath
就可以成功編譯。
此時你想把該 JAR 檔加入到專案中,但是該專案採用 Apache Maven 來管裡所有的相依套件(Dependencies),因此我們必須對該 JAR 檔進行一層抽象化的封裝,以便使用 Maven 的相依管裡機制。
以往,你的專案跟 JAR 檔的相依關係會是緊密耦合的,像這樣:
但是透過 Maven 來管裡相依套件的話,你的專案與 JAR 檔的相依關係會變成以下這樣:
請記得幾個重點:
Maven 的資訊中心就是
pom.xml
檔Maven 的相依資訊被紀錄在
pom.xml
檔的<dependencies>
區段內Maven 的
pom.xml
看不到任何*.jar
的資訊,因為被Dependencies
隔開了 (抽象化),因此打斷了Project
與JAR
的直接相依關係。
還好 Maven 要把一個既有的 JAR 檔封裝成一個 Maven Repository 的 Artifact 非常容易,只要一個命令就可以完成:
這個命令是直接將 C:\Program Files\Java\jdk1.6.0_43\jre\lib\plugin.jar
封裝成一個 groupId
為 com.duotify
、 artifactId
為 jre6-plugin
與 version
為 1.0.0
且封裝類型為 jar
的 artifact
,並且在不指定 Maven Repository 的情況下,預設就會安裝到 Maven 的本地儲存庫( local repository
),也就是 ~/.m2/
這個目錄!
當你的 Maven 本地儲存庫已經擁有 jre6-plugin-1.0.0 (com.duotify)
這個相依套件,你就可以在 pom.xml
檔案中加入以下相依設定,就可以自動參考進來,建置專案的時候 Maven 也會自動將該套件的 jre6-plugin-1.0.0.jar
檔加入到 javac
編譯時的 -classpath
之中:
而這,就是 Maven 相依管裡的魅力! :+1:
如我們文章一開始說的,如果客戶目前的開發環境會從他們公司自架的 Maven Repository 下載套件,其實也會安裝到本地的 Maven Repository 才對 ( ~/.m2/
),所以客戶大可將他自己的 ~/.m2/
資料夾整個壓縮起來傳給我們,我們只要解壓縮到開發人員本機的 ~/.m2/
目錄,就可以取得完整的相依套件,就可以在本地開發了。但是你必須用「離線」的方式執行 mvn
命令,加上一個 -o
參數即可。例如:
將私有的 JAR 檔加入到 Maven 專案儲存庫
上述作法只能把 JAR 檔安裝到「本地儲存庫」之中,然而如果你有一整個開發團隊,每個人都需要用到這個 plugin.jar
的話,那麼每個人都需要手動安裝一遍,相當不方便。不過再怎麼不方便,其實一台電腦也只需要跑一次而已,影響不會太大,文件寫清楚也可以。
另一種解決方法,就是直接在「專案」內直接建立好一個資料夾 (假設為 libs
資料夾),並把這個資料夾視為一個「遠端儲存庫」來用,然後你可以將 plugin.jar
這個透過 mvn install:install-file
命令,自動將其加入到我稱做「專案儲存庫」的地方,你只要記得將該目錄加入版控即可。以下是執行命令範例:
這裡的 -DlocalRepositoryPath=libs
就是用來將 artifact
安裝到 libs
專案儲存庫之中!
當你在專案的 libs
建立一個「遠端儲存庫」且已擁有 jre6-plugin-1.0.0 (com.duotify)
這個相依套件,你就可以在 pom.xml
檔案中加入 <repositories>
設定,明確指定遠端 Maven Repository 的位址:
如此一來,就不會有人遇到抓不到 JAR 檔的問題了! :+1:
评论