Maven 的依賴
在上一節(jié)中,我們重點介紹了 Maven 的項目對象模型(POM),本節(jié)我們重點介紹另一個重要概念–依賴。我們會介紹什么是依賴,以及在我們平時的工作中的最佳實踐。
1. 何為依賴?
依賴即為本項目對其他項目的引用,這里的其他項目可以是外部項目,也可以是內部項目。我們在開發(fā)項目的過程中,將其他項目作為依賴引用進來,最終在打包的過程中,依賴會和我們開發(fā)的項目打包到一起來運行。
在我們的項目沒有使用 Maven 的時候,我們需要手動去管理我們的依賴,例如:添加依賴,刪除依賴。在使用了 Maven 之后,我們可以在 pom.xml 文件里面看到我們所有的依賴,并且可以靈活的管理項目的依賴。
在上圖中,我們可以清晰的看到我們項目目前所引用的依賴。
1.1 依賴的范圍
Maven 在編譯和運行以及執(zhí)行測試用例的時候,分別會使用不同的 classpath。而 Maven 的依賴范圍則是用來控制依賴與不同 classpath 關系的。
Maven 的依賴范圍分為以下幾種:
- compile: 編譯依賴范圍。Maven 默認的依賴范圍,該范圍的依賴對編譯,運行,測試不同的classpath 都有效。例如我們項目中的 spring-boot-starter;
- test: 測試依賴范圍。該依賴范圍只對測試 classpath 有效,在編譯項目或者運行項目的時候,是無法使用此類依賴的。例如我們項目中的 spring-boot-starter-test;
- provided: 已提供依賴范圍。該 Maven 依賴對于編譯和測試的 classpath 有效,但是在運行時無效;
- runtime: 運行時依賴范圍。顧名思義,該依賴范圍對測試和運行的 classpath 有效,但是在編譯時無效;
- system: 系統(tǒng)依賴范圍。該依賴范圍與 classpath 的關系與 provided 依賴范圍是相同的。但是,在使用時需要謹慎注意,因為此類依賴大多數是與本機綁定的,而不是通過Maven倉庫解析出來的,切換環(huán)境后,可能會導致依賴失效或者依賴錯誤。
2. 傳遞性依賴
目前我們的項目只引用了兩個依賴,spring-boot-starter 和 spring-boot-starter-test,但是是這樣子的嗎?
為了能夠更清晰的看到我們項目依賴的結構,我們可以在 IDEA 里面安裝 Maven Helper 插件。
安裝好之后,我們打開項目的 pom.xml 文件,可以看到在文件的下方多了一個 Dependency Analyzer按鈕。打開之后,如下圖。
從這里我們就可以看到,其實我們不只是引入了兩個包,而是引入了很多個包,這是為什么呢?
答案是因為 Maven 的傳遞性依賴機制。
在我們這個項目中,我們引入了 spring-boot-starter 依賴,并且該依賴的范圍是 compile,但是 spring-boot-starter 作為一個項目也有自己的依賴,在這其中的依賴范圍為 compile 的依賴,則會自動轉換成我們項目的依賴,例如 spring-boot 依賴,logback-core 依賴。
所以,有了 Maven 的傳遞性依賴機制之后,我們在使用一個依賴的時候,就不再需要考慮它又依賴了哪些,而是直接使用即可,其他的事情 Maven 會自動幫我們做完。
3. 最短路徑原則
有了傳遞性依賴能夠大大節(jié)省我們在管理依賴時候所耗費的精力。但是,如果傳遞性依賴出了問題我們應該如何解決呢?首先,我們應該知道的是傳遞性依賴是從哪條依賴路徑引用進來的。
在我們的項目中就存在這樣的例子。我們可以看到如下兩條不同的引用路徑:
1. spring-boot-starter --> spring-boot --> spring-aop --> spring-core
2. spring-boot-starter --> spring-core
這個時候,我們可以看到,兩條路徑最終引用的 spring-core 版本都是 5.2.5-RELEASE。但是如果引用的 spring-core 版本不同,Maven 會怎么做呢?
使用最短路徑原則,路徑2中的 spring-core 版本會本引用,這樣就不會造成重復依賴的問題產生。
4. 最佳實踐
4.1 排除依賴
傳遞性依賴可以幫助我們簡化項目依賴的管理,但是同時也會帶來其他的不必要的風險,例如:會隱式地引入一些依賴,這些依賴可能并不是我們希望引入的,或者這些隱式引入的依賴是 SNAPSHOT 版本的依賴。依賴的不穩(wěn)定導致了我們項目的不穩(wěn)定。
在我們的項目中,spring-boot-starter-test 依賴中排除了 junit-vintage-engine 依賴是由于我們使用的 springboot 版本是 2.2.6-RELEASE,對應的 Junit 版本是 5.x,但 junit-vintage-engine 依賴中包含了 4.x 版本的 Junit,此時我們就可以將該依賴排除。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
在 exclusions 標簽中,可以有多個 exclusion 標簽,用來排除不需要的依賴。
4.2 依賴歸類
在我們實際的開發(fā)過程中,我們可能會需要整合很多第三方框架,在整合這些框架的時候,往往需要在 pom.xml 里面添加多個依賴來完成整合。而這些依賴往往是需要保持相同版本的,在升級框架的時候,都是要統(tǒng)一升級到一個相同的版本。
如下圖,我們可以看到,在引入 dubbo 框架的時候,我們需要引入兩個相關的依賴,而且版本號是相同的,這個時候,我們就可以把對應的版本號提取出來,放到 properties 標簽里面,作為一個全局參數來使用。類似于 Java 語言中抽象的思想。
這時候,我們可以看到,如果在將來的某一天我們需要升級升級 dubbo 框架對應的版本,只需要修改 properties 中的版本號,就能將所有依賴的版本一起升級。
4.3 依賴優(yōu)化
我們再回過頭來看一下 Maven Helper 工具所展示的場景
我們在這個工具中可以看到我們項目現在引入的所有的依賴,可以看到哪些是我們用到的,哪些是沒有用來的,依賴與依賴之間是否存在沖突。如果出現了上述情況,我們就可以通過刪除或者依賴排除的方式來將我們不需要的依賴刪除掉,從而使我們的項目更簡潔。
5. 小結
本節(jié)中,我們介紹了 Maven 中的一個重要的概念–依賴,介紹了什么是依賴,以及依賴的幾個特性,最后我們也總結了在平時的工作中常常會用到的依賴優(yōu)化的方式,能夠幫助我們更好的管理項目的依賴。