基於工程師不重複造輪子的原則,我們在開發專案時經常會引入各種第三方套件來提升開發效率。但試想一個情況:當您引入的套件 A 依賴於套件 B,而套件 B 又依賴於套件 C、D、E...,如果需要手動逐一下載和管理這些依賴關係,這將是一個苦力活!更不用說還要確保版本相容性了。
另一個常見的挑戰是依賴衝突:當套件 A 使用 Logback 1.5.18 版本,而套件 B 卻使用 Logback 1.5.0 版本時,應該如何選擇?
幸好,Maven 的傳遞依賴機制為我們解決了這兩大難題。
當我們在pom.xml設定依賴某個套件,執行Maven時會自動下載該依賴所需要的其他依賴,這些間接依賴就叫做「傳遞依賴」,例如:我們設定了
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.30.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.6</version>
</dependency>
我們可以執行mvn dependency:tree
來查看套件相依的行為,就會發現你的org.springframework依賴關係的樣子
當同一個依賴有多個版本時,選擇依賴路徑最短的版本。
範例:
專案 A
├─ B
│ └─ E 2.0
└─ C
└─ D
└─ E 1.0
在這個例子中,E 有兩個版本:
<dependency>
<groupId>org.owasp.esapi</groupId>
<artifactId>esapi</artifactId>
<version>2.7.0.0</version>
</dependency>
這次我們的指令加入-Dverbose
參數,使用mvn dependency:tree -Dverbose
,會發現被因路徑長度被捨棄使用esapi相依的commons-logging:1.3.5
當路徑長度相同時,使用第一個聲明的版本。
範例:
我們在pom.xml把esapi移除在spring下方加入bean util,來測試路徑長度一樣時的狀況
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.6</version>
</dependency>
執行mvn dependency:tree -Dverbose
發現到會採用spring的commons-logging:1.2
調換兩個聲明順序,執行mvn dependency:tree -Dverbose
,則會使用beanUtil的commons-logging:1.0
當 A 依賴 B,B 又依賴 C 時,C 最終在 A 專案中會是什麼範圍?這就是依賴範圍傳遞的核心問題。
compile | provided | runtime | test | |
---|---|---|---|---|
compile | compile | - | runtime | - |
provided | provided | - | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |
表格說明:
我離不開你,你離不開他,那麼我也沒辦法離開你的概念
<!-- 您直接依賴某個套件 (compile) -->
<dependency>
<groupId>com.example</groupId>
<artifactId>XXX-library</artifactId>
<version>1.0</version>
<scope>compile</scope> <!-- A→B: compile -->
</dependency>
如果 XXX-library 內部依賴某個 database driver (runtime scope):
compile
runtime
runtime
<!-- Web 應用依賴 Spring Web MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.21</version>
<scope>compile</scope> <!-- A→B: compile -->
</dependency>
如果 spring-webmvc 內部依賴 servlet-api (provided scope):
compile
provided
-
(不會傳遞) 因為容器會提供 servlet-api在某些公司習慣使用有大公司Support的Application Server,所以就會產生你用Spring Boot開發但是打包時需要移除它embeded tommcat,我們先透過Spring Initializr創建一個Spring Boot Web專案並加入以下設定,對spring-boot-starter-tomcat
設定依賴範圍為provided,如此一來embeded tommcat就不會被打包
<packaging>war</packaging>
<dependencies>
<!-- 略 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
執行mvn package,於target目錄下產生WAR檔
## 小結
今日說明了依賴傳遞的原則,與依賴範圍傳遞的關係,知道這些關係原則後如果要改變它該如何做呢?這是我們明天的課題