Maven中scope標(biāo)簽詳解

scope元素的作用:控制 dependency 元素的使用范圍,即控制 Jar 包在哪些范圍被加載和使用。具體值如下:
  • compile:默認(rèn)值。表示被依賴項(xiàng)目需要參與當(dāng)前項(xiàng)目的編譯、測(cè)試、打包、運(yùn)行,是一個(gè)比較強(qiáng)的依賴。

  • test:依賴項(xiàng)目?jī)H僅參與測(cè)試相關(guān)的工作。包括測(cè)試代碼的編譯和執(zhí)行,不會(huì)被打包,例如:junit。

  • runtime:表示被依賴項(xiàng)目無需參與項(xiàng)目的編譯,不過參與后期的測(cè)試、打包、運(yùn)行。與compile相比,跳過了編譯。例如JDBC驅(qū)動(dòng),適用運(yùn)行和測(cè)試階段。

  • provided:理論上可以參與編譯,測(cè)試等周期,但不被打包,也不會(huì)參與運(yùn)行,因?yàn)槠渌囊蕾嚂?huì)提供。相比于compile,打包階段做了exclude操作。

  • system:和provided相同。不過被依賴項(xiàng)不會(huì)從maven倉(cāng)庫(kù)下載,而是從本地文件系統(tǒng)拿。需要添加systemPath的屬性來定義路徑。一般用于引用外部Jar包。

  • import:它只使用在<dependencyManagement>中,表示從其它的pom文件導(dǎo)入dependency的配置。

compile(默認(rèn))

含義:compile 是默認(rèn)值,如果沒有指定 scope 值,該元素的默認(rèn)值為 compile。被依賴項(xiàng)目需要參與到當(dāng)前項(xiàng)目的編譯,測(cè)試,打包,運(yùn)行等階段。打包的時(shí)候通常會(huì)包含被依賴項(xiàng)目。

provided

含義:被依賴項(xiàng)目理論上可以參與編譯、測(cè)試、運(yùn)行等階段,相當(dāng)于compile,但是再打包階段做了exclude的動(dòng)作。

例:開發(fā)web的時(shí)候,需要用到servlet-api,于是在pom.xml中添加依賴:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>3.0-alpha-1</version>
</dependency>

通過插件啟動(dòng)tomcat的時(shí)候,報(bào)錯(cuò),里面有一段是這樣的:

Caused by: java.lang.LinkageError: loader constraint violation: loader (instance of org/apache/catalina/loader/WebappClassLoader) previously initiated loading for a different type with name "javax/servlet/ServletContext"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)

產(chǎn)生的原因是:tomcat中也有servlet-api包,這樣,發(fā)生了沖突。

解決方法:添加provided,因?yàn)閜rovided表明該包只在編譯和測(cè)試的時(shí)候用,所以,當(dāng)啟動(dòng)tomcat的時(shí)候,就不會(huì)沖突了,完整依賴如下:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>3.0-alpha-1</version>
    <scope>provided</scope>
</dependency>

因?yàn)閟cope=provided的情況,只影響到編譯,測(cè)試階段,而在運(yùn)行階段,目標(biāo)容器(比如我們這里的tomcat容器)已經(jīng)提供了這個(gè)jar包,所以無需我們這個(gè)artifact對(duì)應(yīng)的jar包了。

再舉個(gè)scope=provided的例子。
比如說,假定我們自己的項(xiàng)目ProjectABC 中有一個(gè)類叫C1,而這個(gè)C1中會(huì)import包portal-impl.jar的類B1,那么在編譯階段,我們肯定需要這個(gè)B1,否則C1通不過編譯。因?yàn)槲覀兊膕cope設(shè)置為provided了,所以編譯階段起作用,所以C1正確的通過了編譯。

那么最后我們要把ProjectABC部署到Liferay服務(wù)器上了,這時(shí)候,我們到$liferay-tomcat-home\webapps\ROOT\WEB-INF\lib下發(fā)現(xiàn),里面已經(jīng)有了一個(gè)portal-impl.jar了,換句話說,容器已經(jīng)提供了這個(gè)jar,所以,我們?cè)谶\(yùn)行階段,這個(gè)C1類直接可以用容器提供的portal-impl.jar中的B1類,而不會(huì)出任何問題。

注:實(shí)際maven install生成最終的構(gòu)件包ProjectABC.war后,在其下的WEB-INF/lib中,會(huì)包含被標(biāo)注為scope=compile的構(gòu)件的jar包,而不會(huì)包含被標(biāo)注為scope=provided的構(gòu)件的jar包。這也避免了此類構(gòu)件當(dāng)部署到目標(biāo)容器后產(chǎn)生包依賴沖突。

runtime

含義:表示被依賴項(xiàng)目無需參與項(xiàng)目的編譯,但是會(huì)參與到項(xiàng)目的測(cè)試和運(yùn)行。與compile相比,被依賴項(xiàng)目無需參與項(xiàng)目的編譯。

適用場(chǎng)景:例如,在編譯的時(shí)候我們不需要 JDBC API 的 jar 包,而在運(yùn)行的時(shí)候我們才需要 JDBC 驅(qū)動(dòng)包。

test

含義: 表示被依賴項(xiàng)目?jī)H僅參與測(cè)試相關(guān)的工作,包括測(cè)試代碼的編譯,執(zhí)行。

適用場(chǎng)景:例如,Junit 測(cè)試。

system

含義:system 元素與 provided 元素類似,但是被依賴項(xiàng)不會(huì)從 maven 倉(cāng)庫(kù)中查找,而是從本地系統(tǒng)中獲取,systemPath 元素用于制定本地系統(tǒng)中 jar 文件的路徑。例如:

<dependency>
    <groupId>org.open</groupId>
    <artifactId>open-core</artifactId>
    <version>1.5</version>
    <scope>system</scope>
    <systemPath>${basedir}/WebContent/WEB-INF/lib/open-core.jar</systemPath>
</dependency>

一般用于Maven項(xiàng)目引入第三方Jar文件。比如使用第三方Jar文件,而指定的遠(yuǎn)程倉(cāng)庫(kù)沒有該Jar文件,可以先把需要的Jar導(dǎo)入到項(xiàng)目,然后pom文件通過 <scope>system</scope> 和 <systemPath>…</systemPath> 指定本地Jar文件。詳細(xì)步驟見 《Intellij IDEA在maven項(xiàng)目中添加外部Jar包運(yùn)行》

import

它只使用在<dependencyManagement>中,表示從其它的pom文件中導(dǎo)入dependency的配置。

例子:SpringBoot應(yīng)用需要繼承父類 spring-boot-starter-parent,需要添加 <parent>。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
</parent>

繼承父模塊spring-boot-starter-parent,然后再引入相應(yīng)的依賴。

假如說,我不想繼承,或者我想繼承多個(gè),怎么做?

我們知道Maven的繼承和Java的繼承一樣,是無法實(shí)現(xiàn)多重繼承的。如果10個(gè)、20個(gè)甚至更多模塊繼承自同一個(gè)模塊,那么按照我們之前的做法,這個(gè)父模塊的dependencyManagement會(huì)包含大量的依賴。如果你想把這些依賴分類以更清晰的管理,那就不可能了。

import scope依賴能解決這個(gè)問題。

你可以把dependencyManagement放到單獨(dú)的專門用來管理依賴的pom中,然后在需要使用依賴的模塊中通過import scope依賴,就可以引入dependencyManagement。

例如可以寫這樣一個(gè)用于依賴管理的pom:tools-1.0.0.pom

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ymqx</groupId>
    <artifactId>tools</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0</version>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.8.2</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.16</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

然后我就可以通過非繼承的方式來引入這段依賴管理配置:

<project>
    ...
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.ymqx</groupId>
                <artifactId>tools</artifactId>
                <version>1.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </dependency>
    </dependencies>
</project>

dependencyManagement中的dependency 指定 groupId:com.ymqx、artifactid:tools、version:1.0.0、type:pom 以及 scope:import 。

dependencies中的dependency就可以引用依賴管理tools-1.0.0.pom 的具體依賴。

這樣,父模塊的pom就會(huì)非常干凈,由專門的packaging為pom來管理依賴,也契合的面向?qū)ο笤O(shè)計(jì)中的單一職責(zé)原則。此外,我們還能夠創(chuàng)建多個(gè)這樣的依賴管理pom,以更細(xì)化的方式管理依賴。這種做法與面向?qū)ο笤O(shè)計(jì)中使用組合而非繼承也有點(diǎn)相似的味道。

注意:import scope只能用在<dependencyManagement>里面

那么,如何用這個(gè)方法來解決SpringBoot的那個(gè)繼承問題呢?
配置如下:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
 
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

這樣配置的話,自己的項(xiàng)目里面就不需要繼承SpringBoot的module了,而可以繼承自己項(xiàng)目的module了。

scope的依賴傳遞

實(shí)際開發(fā)的項(xiàng)目中,各種依賴相互之間的關(guān)系很混亂。不同的依賴范圍會(huì)導(dǎo)致不同的結(jié)果,看一下下面簡(jiǎn)單的例子:


A–>B–>C。當(dāng)前項(xiàng)目為A,A依賴于B,依賴范圍是test;B依賴于C,依賴范圍是compile。那么C在A的classpath下是test作用域。

具體規(guī)則如下:


第一列表示A對(duì)B的scope,第一行表示B對(duì)C的scope。A對(duì)C的scope就是內(nèi)部具體對(duì)應(yīng)的位置所示。橫線表示依賴無法傳遞。

規(guī)則可以總結(jié)如下:

  • 當(dāng)B對(duì)C的scope為 test、provided時(shí),C直接被丟棄,A不依賴C。
  • 當(dāng)B對(duì)C的scope為 compile時(shí),A對(duì)C的scope取決于A對(duì)B的scope。
  • 當(dāng)B對(duì)C的scope為 runtime時(shí),A對(duì)C的scope取決于A對(duì)B的scope,除了compile。

參考文章:
Maven—scope和依賴傳遞的介紹
Maven依賴中的Scope詳解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 最近在項(xiàng)目開發(fā)中發(fā)現(xiàn)有些同學(xué)對(duì)于maven中scope的使用還不太了解,今天給大家詳細(xì)介紹一下。 Maven...
    哈利愛分享閱讀 643評(píng)論 0 4
  • 一、scop的分類 compile:默認(rèn)值 他表示被依賴項(xiàng)目需要參與當(dāng)前項(xiàng)目的編譯,還有后續(xù)的測(cè)試,運(yùn)行周期也參與...
    Cola_Aone閱讀 570評(píng)論 0 0
  • compile:scope的默認(rèn)值是compile,表示被依賴項(xiàng)目需要參與當(dāng)前項(xiàng)目的編譯,還有后續(xù)的測(cè)試,運(yùn)行周期...
    王山而起床寫字啦閱讀 674評(píng)論 0 1
  • Maven的一個(gè)設(shè)計(jì)范式是約定優(yōu)于配置(convention over configuration), Maven...
    weylan閱讀 288評(píng)論 0 0
  • Maven依賴中scope的含義 整理一下Maven中Scope的詳細(xì)作用,都是抄的別人內(nèi)容整理了一下。參考:ht...
    UEUEO閱讀 35,104評(píng)論 7 33

友情鏈接更多精彩內(nèi)容