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。