導語
生産環境下開發不再是一個項目一個工程,而是每一個子產品建立一個工程,而多個子產品整合在一起就需要使用到像 Maven 這樣的建構工具。
1. Why?
1.1. 真的需要嗎?
Maven 是幹什麼用的?這是很多同學在剛開始接觸 Maven 時最大的問題。之是以會提出這個問題,是因為即使不使用 Maven 我們仍然可以進行 B/S 結構項目的開發。從表述層、業務邏輯層到持久化層再到資料庫都有成熟的解決方案——不使用 Maven 我們一樣可以開發項目啊?

這裡給大家糾正一個誤區,Maven 并不是直接用來輔助編碼的,它戰鬥的崗位并不是以上各層。是以我們有必要通過企業開發中的實際需求來看一看哪些方面是我們現有技術的不足。
1.2. 究竟為什麼?
為什麼要使用 Maven ?它能幫助我們解決什麼問題?
-
添加第三方 jar 包
在今天的 JavaEE 開發領域,有大量的第三方架構和工具可以供我們使用。要使用這些 jar 包最簡單的方法就是複制粘貼到 WEB-INF/lib 目錄下。但是這會導緻每次建立一個新的工程就需要将 jar 包重複複制到 lib 目錄下,進而造成工作區中存在大量重複的檔案,讓我們的工程顯得很臃腫。
而使用 Maven 後每個 jar 包本身隻在本地倉庫中儲存一份,需要 jar 包的工程隻需要以坐标的方式簡單的引用一下就可以了。不僅極大的節約了存儲空間,讓項目更輕巧,更避免了重複檔案太多而造成 的混亂。
2.管理 jar包之間的依賴關系
jar 包往往不是孤立存在的,很多 jar 包都需要在其他 jar 包的支援下才能夠正常工作,我們稱之為 jar 包之間的依賴關系。最典型的例子是:commons-fileupload-1.3.jar 依賴于 commons-io-2.0.1.jar,如果 沒有 IO 包,FileUpload 包就不能正常工作。
那麼問題來了,你知道你所使用的所有 jar 包的依賴關系嗎?當你拿到一個新的從未使用過的 jar 包,你如何得知他需要哪些 jar 包的支援呢?如果不了解這個情況,導入的 jar 包不夠,那麼現有的程式将不能正常工作。再進一步,當你的項目中需要用到上百個 jar 包時,你還會人為的,手工的逐一确 認它們依賴的其他 jar 包嗎?這簡直是不可想象的。
而引入 Maven 後,Maven 就可以替我們自動的将目前 jar 包所依賴的其他所有 jar 包全部導入進來, 無需人 工參與,節約了我們大量的時間和精力。 用實際例子來說明就是 : 通 過 Maven 導入 commons-fileupload-1.3.jar後,commons-io-2.0.1.jar會被自動導入,程式員不必了解這個依賴關系。
下圖是 Spring 所需 jar 包的部分依賴關系:
-
擷取第三方jar包
JavaEE 開發中需要使用到的 jar 包種類繁多,幾乎每個 jar 包在其本身的官網上的擷取方式都不盡相 同。為了查找一個 jar 包找遍網際網路,身心俱疲,沒有經曆過的人或許體會不到這種折磨。不僅如此, 費勁心血找的 jar 包裡有的時候并沒有你需要的那個類,又或者又同名的類沒有你要的方法——以不規範的方式擷取的 jar 包也往往是不規範的。
使用 Maven 我們可以享受到一個完全統一規範的 jar 包管理體系。你隻需要在你的項目中以坐标的 方式依賴一個 jar 包,Maven 就會自動從中央倉庫進行下載下傳,并同時下載下傳這個 jar 包所依賴的其他 jar 包——規範、完整、準确!一次性解決所有問題!
Tips:在這裡我們順便說一下,統一的規範幾乎可以說成是程式員的最高信仰。如果沒有統一的規範,就意味着每個具體的技術都各自為政,需要以諸多不同的特殊的方式加入到項目中;好不容易加入進來還會和其他技術格格不入,最終受苦的是我們。而任何一個領域的統一規範都能夠極大的降低程式 員的工作難度,減少工作量。例如:USB 接口可以外接各種裝置,如果每個裝置都有自己獨特的接口, 那麼不僅制造商需要維護各個接口的設計方案,使用者也需要詳細了解每個裝置對應的接口,無疑是非常繁瑣的。
-
将項目拆分成多個工程子產品
随着 JavaEE 項目的規模越來越龐大,開發團隊的規模也與日俱增。一個項目上千人的團隊持續開 發很多年對于 JavaEE 項目來說再正常不過。那麼我們想象一下:幾百上千的人開發的項目是同一個 Web 工程。那麼架構師、項目經理該如何劃分項目的子產品、如何分工呢?這麼大的項目已經不可能通過 package 結構來劃分子產品,必須将項目拆分成多個工程協同開發。多個子產品工程中有的是 Java 工程,有 的是 Web 工程。那麼工程拆分後又如何進行互相調用和通路呢?這就需要用到 Maven 的依賴管理機制。大家請看我們的 Survey 調查項目拆分的情況:
上層子產品依賴下層,是以下層子產品中定義的 API 都可以為上層所調用和通路。自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站
2. What?
2.1. Maven 簡介
Maven 是 Apache 軟體基金會組織維護的一款自動化建構工具,專注服務于 Java 平台的項目建構和依賴管理。Maven這個單詞的本意是:專家,内行。讀音是['meɪv(ə)n]或['mevn]。
2.2. 什麼是建構?
建構并不是建立,建立一個工程并不等于建構一個項目。要了解建構的含義我們應該由淺入深的從以下三個層面來看:
-
純 Java 代碼
大家都知道,我們 Java 是一門編譯型語言,.java 擴充名的源檔案需要編譯成 .class 擴充名的位元組碼檔案才能夠執行。是以編寫任何 Java 代碼想要執行的話就必須經過編譯得到對應的 .class 檔案。
-
Web 工程
當我們需要通過浏覽器通路 Java 程式時就必須将包含 Java 程式的 Web 工程編譯的結果“拿”到伺服器上的指定目錄下,并啟動伺服器才行。這個“拿”的過程我們叫部署。
我們可以将未編譯的 Web 工程比喻為一隻生的雞,編譯好的 Web 工程是一隻煮熟的雞,編譯部署的過程就是将雞炖熟。
Web 工程和其編譯結果的目錄結構對比見下圖:
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站 -
實際項目
在實際項目中整合第三方架構,Web 工程中除了 Java 程式和 JSP 頁面、圖檔等靜态資源之外,還 包括第三方架構的 jar 包以及各種各樣的配置檔案。所有這些資源都必須按照正确的目錄結構部署到伺服器上,項目才可以運作。
是以綜上所述:建構就是以我們編寫的 Java 代碼、架構配置檔案、國際化等其他資源檔案、JSP 頁面和圖檔等靜态資源作為“原材料”,去“生産”出一個可以運作的項目的過程。
那麼項目建構的全過程中都包含哪些環節呢?
2.3. 建構過程的幾個主要環節
- 清理:删除以前的編譯結果,為重新編譯做好準備。
- 編譯:将 Java 源程式編譯為位元組碼檔案。
- 測試:針對項目中的關鍵點進行測試,確定項目在疊代開發過程中關鍵點的正确性。
- 報告:在每一次測試後以标準的格式記錄和展示測試結果。
- 打包:将一個包含諸多檔案的工程封裝為一個壓縮檔案用于安裝或部署。Java 工程對應 jar 包,Web 工程對應 war 包。
- 安裝:在 Maven 環境下特指将打包的結果——jar 包或 war 包安裝到本地倉庫中。
- 部署:将打包的結果部署到遠端倉庫或将 war 包部署到伺服器上運作。
2.4. 自動化建構
其實上述環節我們在 Eclipse 中都可以找到對應的操作,隻是不太标準。那麼既然 IDE 已經可以進行建構了我們為什麼還要使用 Maven 這樣的建構工具呢?我們來看一個小故事:
這是陽光明媚的一天。托馬斯向往常一樣早早的來到了公司,沖好一杯咖啡,進入了自己的郵箱——很不幸,QA小組發來了一封郵件,報告了他昨天送出的子產品的測試結果——有 BUG。“好吧,反正也不是第一 次”,托馬斯搖搖頭,進入IDE,運作自己的程式,編譯、打包、部署到伺服器上,然後按照郵件中的操作路徑進行測試。“嗯,沒錯,這個地方确實有問題”,托馬斯說道。于是托馬斯開始嘗試修複這個 BUG,當他差不多有眉目的時候已經到了午飯時間。
下午繼續工作。BUG 很快被修正了,接着托馬斯對子產品重新進行了編譯、打包、部署,測試之後确認沒有問題了,回複了 QA 小組的郵件。
一天就這樣過去了,明媚的陽光化作了美麗的晚霞,托馬斯卻覺得生活并不像晚霞那樣美好啊。
讓我們來梳理一下托馬斯這一天中的工作内容:
從中我們發現,托馬斯的很大一部分時間花在了“編譯、打包、部署、測試”這些程式化的工作上面,而真正需要由“人”的智慧實作的分析問題和編碼卻隻占了很少一部分。
能否将這些程式化的工作交給機器自動完成呢?——當然可以!這就是自動化建構。
此時 Maven 的意義就展現出來了,它可以自動的從建構過程的起點一直執行到終點:
2.5. Maven 核心概念
Maven 能夠實作自動化建構是和它的内部原理分不開的,這裡我們從 Maven 的九個核心概念入手,看看Maven 是如何實作自動化建構的。
- POM
- 約定的目錄結構
- 坐标
- 依賴管理
- 倉庫管理
- 生命周期
- 插件和目标
- 繼承
- 聚合
3. How?
Maven 的核心程式中僅僅定義了抽象的生命周期,而具體的操作則是由 Maven 的插件來完成的。可是Maven 的插件并不包含在 Maven 的核心程式中,在首次使用時需要聯網下載下傳。 下載下傳得到的插件會被儲存到本地倉庫中。本地倉庫預設的位置是:~.m2\repository。
4. 約定的目錄結構
約定的目錄結構對于 Maven 實作自動化建構而言是必不可少的一環,就拿自動編譯來說,Maven 必須能找到Java源檔案,下一步才能編譯,而編譯之後也必須有一個準确的位置保持編譯得到的位元組碼檔案。
我們在開發中如果需要讓第三方工具或架構知道我們自己建立的資源在哪,那麼基本上就是兩種方式:
- 通過配置的形式明确告訴它
-
基于第三方工具或架構的約定
Maven對工程目錄結構的要求就屬于後面的一種。
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站
現在 JavaEE 開發領域普遍認同一個觀點:約定>配置>編碼。意思就是能用配置解決的問題就不編碼, 能基于約定的就不進行配置。而 Maven 正是因為指定了特定檔案儲存的目錄才能夠對我們的 Java 工程進行 自動化建構。
5. POM
Project Object Model:項目對象模型。将 Java 工程的相關資訊封裝為對象作為便于操作和管理的模型。
Maven 工程的核心配置。可以說學習 Maven 就是學習 pom.xml 檔案中的配置。
6. 坐标
6.1. 幾何中的坐标
- 在一個平面中使用 x、y 兩個向量可以唯一的确定平面中的一個點。
- 在空間中使用 x、y、z 三個向量可以唯一的确定空間中的一個點。
6.2. Maven的坐标
使用如下三個向量在 Maven 的倉庫中唯一的确定一個 Maven 工程。
- groupid:公司或組織的域名倒序+目前項目名稱。
- artifactId:目前項目的子產品名稱。
- version:目前子產品的版本。
<groupId>com.atguigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
6.3. 如何通過坐标到倉庫中查找 jar 包?
- 将 gav 三個向量連起來
com.atguigu.maven+Hello+0.0.1-SNAPSHOT
- 以連起來的字元串作為目錄結構到倉庫中查找
com/atguigu/maven/Hello/0.0.1-SNAPSHOT/Hello-0.0.1-SNAPSHOT.jar
注意:我們自己的 Maven 工程必須執行安裝操作才會進入倉庫。安裝的指令是:mvn install
7. 依賴
Maven 中最關鍵的部分,我們使用 Maven 最主要的就是使用它的依賴管理功能。要了解和掌握 Maven
的依賴管理,我們隻需要解決一下幾個問題:
-
依賴的目的是什麼
當 A jar 包用到了 B jar 包中的某些類時,A 就對 B 産生了依賴,這是概念上的描述。那麼如何在項目中以依賴的方式引入一個我們需要的 jar 包呢?
答案非常簡單,就是使用 dependency 标簽指定被依賴 jar 包的坐标就可以了。
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
-
依賴的範圍
大家注意到上面的依賴資訊中除了目标 jar 包的坐标還有一個 scope 設定,這是依賴的範圍。依賴的範 圍有幾個可選值,我們用得到的是:compile、test、provided 三個。
- 從項目結構角度了解 compile 和 test 的差別
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站 結合具體例子:對于 HelloFriend 來說,Hello 就是服務于主程式的,junit 是服務于測試程式的。HelloFriend 主程式需要 Hello 是非常明顯的,測試程式由于要調用主程式是以也需要 Hello,是以
compile 範圍依賴對主程式和測試程式都應該有效。
HelloFriend 的測試程式部分需要 junit 也是非常明顯的,而主程式是不需要的,是以 test 範圍依賴 僅僅對于主程式有效。
- 從開發和運作這兩個不同階段了解 compile 和 provided 的差別
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站 - 有效性總結
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站
-
依賴的傳遞性
A 依賴 B,B 依賴 C,A 能否使用 C 呢?那要看 B 依賴 C 的範圍是不是 compile,如果是則可用,否則不 可用。
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站 -
依賴的排除
如果我們在目前工程中引入了一個依賴是 A,而 A 又依賴了 B,那麼 Maven 會自動将 A 依賴的 B 引入當 前工程,但是個别情況下 B 有可能是一個不穩定版,或對目前工程有不良影響。這時我們可以在引入 A 的時 候将 B 排除。
- 情景舉例
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站 - 排除方式
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>HelloFriend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
- 排除後的效果
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站
-
統一管理所依賴 jar 包的版本
對同一個架構的一組 jar 包最好使用相同的版本。為了友善更新架構,可以将 jar 包的版本資訊統一提取出來 。
- 統一聲明版本号
<properties>
<atguigu.spring.version>4.1.1.RELEASE</atguigu.spring.version>
</properties>
其中 atguigu.spring.version 部分是自定義标簽。
- 引用前面聲明的版本号
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
……
</dependencies>
- 其他用法
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
- 依賴的原則:解決 jar 包沖突
- 路徑最短者優先
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站 - 路徑相同時先聲明者優先 這裡“聲明”的先後順序指的是 dependency 标簽配置的先後順序。
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站
8. 倉庫
8.1. 分類
- 本地倉庫:為目前本機電腦上的所有 Maven 工程服務。
- 遠端倉庫
- 私服:架設在目前區域網路環境下,為目前區域網路範圍内的所有 Maven 工程服務。
自動化建構工具—Maven導語1. Why?2. What?3. How?4. 約定的目錄結構5. POM6. 坐标7. 依賴8. 倉庫9. 生命周期10. 插件和目标11. 繼承12. 聚合13. Maven酷站 - 中央倉庫:架設在 Internet 上,為全世界所有 Maven 工程服務。
- 中央倉庫的鏡像:架設在各個大洲,為中央倉庫分擔流量。減輕中央倉庫的壓力,同時更快的響應使用者請求。
8.2. 倉庫中的檔案
- Maven 的插件
- 我們自己開發的項目的子產品
-
第三方架構或工具的 jar 包
不管是什麼樣的 jar 包,在倉庫中都是按照坐标生成目錄結構,是以可以通過統一的方式查詢或依賴。
9. 生命周期
9.1. 什麼是 Maven 的生命周期?
- Maven 生命周期定義了各個建構環節的執行順序,有了這個清單,Maven 就可以自動化的執行建構指令了。
- Maven有三套互相獨立的生命周期,分别是:
- Clean Lifecycle:在進行真正的建構之前進行一些清理工作。
- Default Lifecycle:建構的核心部分,編譯,測試,打包,安裝,部署等等。
- Site Lifecycle:生成項目報告,站點,釋出站點。
它們是互相獨立的,你可以僅僅調用 clean 來清理工作目錄,僅僅調用 site 來生成站點。當然你也可以直接運作 mvn clean install site 運作所有這三套生命周期。
每套生命周期都由一組階段(Phase)組成,我們平時在指令行輸入的指令總會對應于一個特定的階段。比 如,運作 mvn clean,這個 clean 是 Clean 生命周期的一個階段。有 Clean 生命周期,也有 clean 階段。
9.2. Clean 生命周期
Clean 生命周期一共包含了三個階段:
- pre-clean:執行一些需要在 clean 之前完成的工作。
- clean:移除所有上一次建構生成的檔案。
- post-clean:執行一些需要在 clean 之後立刻完成的工作。
9.3. Site 生命周期
- pre-site:執行一些需要在生成站點文檔之前完成的工作。
- site:生成項目的站點文檔。
- post-site:執行一些需要在生成站點文檔之後完成的工作,并且為部署做準備。
- site-deploy:将生成的站點文檔部署到特定的伺服器上。
這裡經常用到的是 site 階段和 site-deploy 階段,用以生成和釋出 Maven 站點,這可是 Maven 相當強大的功能,Manager比較喜歡,文檔及統計資料自動生成,很好看。
9.4. Default 生命周期
Default 生命周期是 Maven 生命周期中最重要的一個,絕大部分工作都發生在這個生命周期中。這裡,隻解釋一些比較重要和常用的階段:
validate
generate-sources
process-sources
generate-resources
process-resources:複制并處理資源檔案,至目标目錄,準備打包。
compile:編譯項目的源代碼。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources:複制并處理資源檔案,至目标測試目錄。
test-compile 編譯測試源代碼。
process-test-classes
test:使用合适的單元測試架構運作測試。這些測試代碼不會被打包或部署。
prepare-package
package:接受編譯好的代碼,打包成可釋出的格式,如 JAR。
pre-integration-test
integration-test
post-integration-test
verify
install:将包安裝至本地倉庫,以讓其它項目依賴。
deploy:将最終的包複制到遠端的倉庫,以讓其它開發人員與項目共享或部署到伺服器上運作。
9.5. 生命周期與自動化建構
運作任何一個階段的時候,它前面的所有階段都會被運作,例如我們運作 mvn install 的時候,代碼會被編譯,測試,打包。這就是 Maven 為什麼能夠自動執行建構過程的各個環節的原因。此外,Maven 的插件機制是完全依賴 Maven 的生命周期的,是以了解生命周期至關重要。
10. 插件和目标
- Maven 的核心僅僅定義了抽象的生命周期,具體的任務都是交由插件完成的。
- 每個插件都能實作多個功能,每個功能就是一個插件目标。
-
Maven 的生命周期與插件目标互相綁定,以完成某個具體的建構任務。
例如:compile 就是插件 maven-compiler-plugin 的一個目标;pre-clean 是插件 maven-clean-plugin 的一個目标。
11. 繼承
11.1. 為什麼需要繼承機制?
由于非 compile 範圍的依賴資訊是不能在“依賴鍊”中傳遞的,是以有需要的工程隻能單獨配置。例如:
此時如果項目需要将各個子產品的 junit 版本統一為 4.9,那麼到各個工程中手動修改無疑是非常不可取的。
使用繼承機制就可以将這樣的依賴資訊統一提取到父工程子產品中進行統一管理。
11.2. 建立父工程
建立父工程和建立一般的 Java 工程操作一緻,唯一需要注意的是:打包方式處要設定為 pom。
11.3. 在子工程中引用父工程
<parent>
<!-- 父工程坐标 -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<relativePath>從目前目錄到父項目的 pom.xml 檔案的相對路徑</relativePath>
</parent>
<parent>
<groupId>com.atguigu.maven</groupId>
<artifactId>Parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 指定從目前子工程的pom.xml檔案出發,查找父工程的pom.xml的路徑 -->
<relativePath>../Parent/pom.xml</relativePath>
</parent>
此時如果子工程的 groupId 和 version 如果和父工程重複則可以删除。
11.4. 在父工程中管理依賴
将 Parent 項目中的 dependencies 标簽,用 dependencyManagement 标簽括起來。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
在子項目中重新指定需要的依賴,删除範圍和版本号。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
12. 聚合
12.1. 為什麼要使用聚合?
将多個工程拆分為子產品後,需要手動逐個安裝到倉庫後依賴才能夠生效。修改源碼後也需要逐個手動進行 clean 操作。而使用了聚合之後就可以批量進行 Maven 工程的安裝、清理工作。
12.2. 如何配置置聚合?
在總的聚合工程中使用 modules/module 标簽組合,指定子產品工程的相對路徑即可。
<modules>
<module>../Hello</module>
<module>../HelloFriend</module>
<module>../MakeFriends</module>
</modules>
13. Maven酷站
我們可以到http://mvnrepository.com/搜尋需要的 jar 包的依賴資訊。