Maven中optional和scope元素的使用,你弄明白了?

程式新視界發表於2020-12-02

在梳理專案的過程中發現很多開發同學對Maven依賴檔案的配置並不瞭解,特別是對Maven的optional元素和scope元素的使用也非常隨意。這就會導致釋出的jar包或war包非常“胖”、編譯速度慢,而且還很容易生產jar衝突等問題。本篇文章從optional和scope的使用場景入手,讓專案實現一波瘦身。

optional元素

這裡以Spring Boot專案中的使用為例,比如我們在專案中經常使用的熱部署元件spring-boot-devtools,就可以使用optional元素來進行定義,對應pom檔案中配置如下:

<!--devtools 熱部署-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

那麼,這裡的optional元素設定為true表示何意?optional是Maven依賴jar時的一個選項,表示該依賴是可選的,專案之間依賴不傳遞。不設定optional(預設)或者optional是false,表示傳遞依賴。

文字描述可能比較抽象,下面用具體例項場景來進行更直觀的描述,這裡假設有兩個專案A和B,其中A為父專案,B為子專案。在父專案中引入了單元測試的依賴:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

下面針對optional元素的不同值進行講解。

optional元素預設值(false)

當父專案新增junit依賴時,並未新增optional選項,也就是預設的optional元素的值為false。

此時,當子專案依賴父專案時,父專案A和子專案B的關係如下:
在這裡插入圖片描述

父專案並未設定optional元素為true,那麼便具有依賴傳遞性。此時,子專案B中會直接引入父專案A中引入的Junit的jar包。也就是說B專案打包時,jar/war包中會包含junit的jar包。

optional元素為true

當父專案引入junit依賴時,設定optional元素為true。那麼,子專案B便有了更多的選擇。
在這裡插入圖片描述

如果專案B不需要Junit的jar包,那麼在其pom檔案中不需進行任何處理便可以。如果B專案也需要對應的jar包依賴,可以有兩種選擇:第一、A專案中對應依賴的optional設定為false或去掉;第二、B專案中直接引入需要的該依賴。

parent繼承的情況

我們經常會在parent專案中配置統一的依賴版本控制,如下:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <optional>true</optional>
        </dependency>
    </dependencies>
</dependencyManagement>

此時,如果B專案需要Junit的jar包,可以直接在專案中引入,這裡父專案中的optional配置對子專案並無影響。

綜上所述,在Maven專案中,恰當的使用optional配置,可以在很大程度上減少jar包的大小,提升編譯和釋出速度。

scope元素

上面講完了optional元素的使用,再來看看scope的使用。

scope元素主要用來控制依賴的使用範圍,指定當前包的依賴範圍和依賴的傳遞性,也就是哪些依賴在哪些classpath中可用。常見的可選值有:compile, provided, runtime, test, system等。

compile(編譯)

預設值。compile表示對應依賴會參與當前專案的編譯、測試、執行等,是一個比較強的依賴。打包時通常會包含該依賴,部署時會打包到lib目錄下。比如:spring-core這些核心的jar包。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

test(測試)

scope為test表示依賴專案僅參與測試環節,在編譯、執行、打包時不會使用。最常見的使用就是單元測試類了:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

類似單元測試這樣的依賴,如果不設定scope為test,很顯然它們會被打包、釋出,但其實真是環境中並無什麼作用。

runntime(執行時)

runntime僅僅適用於執行和測試環節,在編譯環境下不會被使用。比如編譯時只需要JDBC API的jar,而只有執行時才需要JDBC驅動實現。

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
    <scope>runtime</scope>
</dependency>

provided(已提供)

provided適合在編譯和測試的環境,和compile功能相似,但provide僅在編譯和測試階段生效,provide不會被打包,也不具有傳遞性。

比如:上面講到的spring-boot-devtools、servlet-api等,前者是因為不需要在生產中熱部署,後者是因為容器已經提供,不需要重複引入。

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <scope>provided</scope>
</dependency>

system

system範圍依賴與provided類似,不過依賴項不會從maven倉庫獲取,而需要從本地檔案系統提供。使用時,一定要配合systemPath屬性。不推薦使用,儘量從Maven庫中引用依賴。

<dependency>
  <groupId>sun.jdk</groupId>
  <artifactId>tools</artifactId>
  <version>1.5.0</version>
  <scope>system</scope>
  <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

scope依賴的傳遞性

上面講解scope的不同引數值,針對這些引數值,在多個專案中的依賴傳遞性如下:
在這裡插入圖片描述

其中B依賴A,C依賴B,傳遞性的關鍵是B依賴A時所設定的scope值,當B採用不同的值時對應的依賴關係如下:當B通過test或provided依賴A時,C不依賴A;當B通過runtime或compile依賴A時,C依賴A;

小結

經過上面對Maven的optional和scope元素的詳細講解,想必你已經可以對專案中的配置進行排查,確保精確的使用專案與專案之間的依賴,而不是每個專案都引入大而全的依賴檔案,導致專案臃腫,編譯緩慢,衝突不斷。

原文連結:《Maven中optional和scope元素的使用,你弄明白了?


程式新視界

公眾號“ 程式新視界”,一個讓你軟實力、硬技術同步提升的平臺,提供海量資料

微信公眾號:程式新視界

相關文章