maven——依賴管理
管理包依賴是 Maven 核心功能之一,下面通過如何引入 jar 包;如何解析 jar 包依賴;包沖突是如何產生;如何解決包沖突;依賴管理解決什麽問題;什麽是依賴範圍;使用包依賴的最佳實踐等 6 個問題來介紹。
如何引入 jar 包
在代碼開發時,如果需要使用第三方 jar 包提供的類庫,那麽需要在 pom.xml 加入該 jar 包依賴。
例如:使用 zookeeper client
<dependencies> <!-- https://mvnrepository.com/artifact/org.apache.hadoop/zookeeper --> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>zookeeper</artifactId> <version>3.3.1</version> </dependency> </dependencies>
Maven 如何解析 jar 包依賴——傳遞依賴
如上所述,在 pom.xml 中引入 zookeeper jar 包依賴,當 Maven 解析該依賴時,需要引入的 jar 包不僅僅只有 zookeeper,還會有 zookeeper 內部依賴的 jar 包,還會有 zookeeper 內部依賴的 jar 包依賴的 jar 包......,依賴關系不斷傳遞,直至沒有依賴。
例如:上述 pom.xml 引入 zookeeper 依賴,實際引入的 jar 包有:
zookeeper jar 依賴項
包沖突如何產生?
舉個??:假設 A->B->C->D1, E->F->D2,D1,D2 分別為 D 的不同版本。
如果 pom.xml 文件中引入了 A 和 E 之後,按照 Maven 傳遞依賴原則,工程內需要引入的實際 Jar 包將會有:A B C D1 和 E F D2,因此 D1,D2 將會產生包沖突。
如何解決包沖突
Maven 解析 pom.xml 文件時,同一個 jar 包只會保留一個,這樣有效的避免因引入兩個 jar 包導致的工程運行不穩定性。
Maven 默認處理策略
- 最短路徑優先
Maven 面對 D1 和 D2 時,會默認選擇最短路徑的那個 jar 包,即 D2。E->F->D2 比 A->B->C->D1 路徑短 1。 - 最先聲明優先
如果路徑一樣的話,舉個??: A->B->C1, E->F->C2 ,兩個依賴路徑長度都是 2,那麽就選擇最先聲明。
移除依賴
如果我們不想通過 A->B->->D1 引入 D1 的話,那麽我們在聲明引入 A 的時候將 D1 排除掉,這樣也避免了包沖突。
舉個??:將 zookeeper 的 jline 依賴排除
<dependency> <groupId>org.apache.hadoop</groupId> <artifactId>zookeeper</artifactId> <version>3.3.1</version> <exclusions> <exclusion> <groupId>jline</groupId> <artifactId>jline</artifactId> </exclusion> </exclusions> </dependency>
檢測包沖突工具
mvn dependency:help
mvn dependency:analyze
mvn dependency:tree
mvn dependency:tree -Dverbose
詳細參考:
mvn dependency
mvn dependency:tree
依賴管理解決什麽問題
當同一個工程內有多個模塊時,並且要求多個模塊使用某個 jar 包的相同版本,為了方便統一版本號,升級版本號,需要提取出一個父親模塊來管理子模塊共同依賴的 jar 包版本。
舉個??:有兩個模塊 projectA, projectB,它們的依賴分別如下所示:
projectA:
<project>
...
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
projectB:
<project>
...
<dependencies>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
projectA 和 projectB 共同依賴了 group-a/artifact-b/1.0,提取公共依賴,生成 parent, parent 依賴如下:
<project>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
則 projectA 和 projectB 均不需要指定 group-a/artifact-b 的 version 信息,未來升級 version 信息時,只需要在 parent 內部指定。
projectA:
<project>
...
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
</dependency>
</dependencies>
</project>
projectB:
<project>
...
<dependencies>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
</dependency>
</dependencies>
</project>
依賴範圍
如果不顯示執行 <scope> 屬性時,默認 <scope>compile</scope>。
scope 有哪些屬性:compile, provided, runtime, test, system 等。
詳細參考:傳送門
最佳實踐
(1)項目中源代碼使用的 jar 包一定在 pom.xml 中顯示引用。
(2)經常 check 一下包沖突,檢查是否需要處理。
(3)當使用多個模塊時,parent 一定要使用包管理模塊來規範 Jar 包版本,而不是包依賴模塊直接引入依賴。 dependencyManagement vs dependencies
說明:
使用dependencyManagement可以統一管理項目的版本號,確保應用的各個項目的依賴和版本一致,不用每個模塊項目都弄一個版本號,不利於管理,當需要變更版本號的時候只需要在父類容器裏更新,不需要任何一個子項目的修改;如果某個子項目需要另外一個特殊的版本號時,只需要在自己的模塊dependencies中聲明一個版本號即可。子類就會使用子類聲明的版本號,不繼承於父類版本號。
與dependencies區別:
1)Dependencies相對於dependencyManagement,所有生命在dependencies裏的依賴都會自動引入,並默認被所有的子項目繼承。
2)dependencyManagement裏只是聲明依賴,並不自動實現引入,因此子項目需要顯示的聲明需要用的依賴。如果不在子項目中聲明依賴,是不會從父項目中繼承下來的;只有在子項目中寫了該依賴項,並且沒有指定具體版本,才會從父項目中繼承該項,並且version和scope都讀取自父pom;另外如果子項目中指定了版本號,那麽會使用子項目中指定的jar版本。
maven——依賴管理