目录
- 背景
- 第一部分 scope标签作用
- 第二部分 打包
- 参考文献及资料
背景
我们在使用Maven
管理包依赖的时候,经常看到下面的配置(一个Flink Java
项目的pom
文件片段):
1 | <dependency> |
其中<scope>provided</scope>
标签参数是什么含义呢?
我们在编码过程通常有:程序测试、程序运行、程序编译等阶段,不同阶段依赖的包并不完全相同。即依赖包是有依赖作用范围的。最直观的例子是我们经常使用的测试依赖包junit
,我们只在测试阶段会使用。所以限定依赖包的生效范围为:test
,参考下面的配置方式。
1 | <dependency> |
接下来我们将详细展开讲解。
第一部分 scope标签作用
1.1 六种依赖范围
通过背景介绍,我们知道Maven
中将使用 scope
标签参数来配置依赖包的依赖范围。scope
主要是用在 pom.xml
文件中的依赖定义部分。常见的可选值有compile, provided, runtime, test, system
,一共6个。
compile
这是默认参数,如果用户没有指定时,scope
标签值为compile
。编译依赖项在项目的编译、运行、测试所有阶段均有效。是一个比较强的依赖。打包的时候通常包含进去。provided
这很像compile
,但表示您希望 JDK 或容器在运行时提供依赖项。例如,在为 Java 企业版构建 Web 应用程序时,您可以将 Servlet API 和相关 Java EE API 的依赖设置为范围provided
,因为 Web 容器提供了这些类。具有此范围的依赖项被添加到用于编译和测试的类路径中,而不是运行时类路径中。它不是传递的。provided意味着打包的时候可以不用包进去,别的设施(Web Container)会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是在打包阶段做了exclude的动作。
runtime
classpaths
这个范围表示编译时不需要依赖,但测试和运行有效。Maven在运行时和测试类路径中包含具有此范围的依赖项,但不包括编译类路径。
runntime表示被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。与compile相比,跳过编译而已,说实话在终端的项目(非开源,企业内部系统)中,和compile区别不是很大。比较常见的如JSR×××的实现,对应的API jar是compile的,具体实现是runtime的,compile只需要知道接口就足够了。oracle jdbc驱动架包就是一个很好的例子,一般scope为runntime。另外runntime的依赖通常和optional搭配使用,optional为true。我可以用A实现,也可以用B实现。
范围是为了
runtime
防止程序员在代码中向实现库添加直接依赖项,而不是使用抽象或外观。换句话说,它强制使用接口。
具体例子:
1)您的团队正在使用 SLF4J 而不是 Log4j。您希望您的程序员使用 SLF4J API,而不是 Log4j。Log4j 仅供 SLF4J 在内部使用。解决方案:
- 将 SLF4J 定义为常规编译时依赖项
- 将 log4j-core 和 log4j-api 定义为运行时依赖项。
2) 您的应用程序正在使用 JDBC 访问 MySQL。您希望您的程序员针对标准 JDBC 抽象进行编码,而不是直接针对 MySQL 驱动程序实现。
mysql-connector-java
将(MySQL JDBC 驱动程序)定义为运行时依赖项。运行时依赖项在编译期间被隐藏(如果您的代码对它们具有“直接”依赖关系,则会引发编译时错误),但在执行期间和创建可部署工件(WAR 文件、SHADED jar 文件等)时包含在内。
1 | <!-- From Log4j 2.x API to Log4j 2.x Core --> |
1 | <!-- For DataBase connection --> |
test
这个范围表示应用程序的正常使用不需要依赖,只在测试编译和执行阶段可用。这个范围不是传递的。通常,此范围用于测试库,例如 JUnit 和 Mockito。如果这些库用于单元测试 (src/test/java) 但不在模型代码 (src/main/java) 中,它也用于非测试库,例如 Apache Commons IO。scope为test表示依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。比较典型的如junit。
system
范围类似于provided
,除了你必须提供明确包含它的 JAR 之外。工件始终可用,不会在存储库中查找。从参与度来说,也provided相同,不过被依赖项不会从maven仓库抓,而是从本地文件系统拿,一定需要配合systemPath属性使用。
1
2
3
4
5
6
7<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
import(only available in Maven 2.0.9 or later)
此范围仅受该部分中类型的依赖项支持。它表示依赖项将被指定 POM部分中的有效依赖项列表替换。由于它们被替换,范围为 的依赖项实际上并不参与限制依赖项的传递性。
pom`
<dependencyManagement>
import`
test
scope为test表示依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。比较典型的如junit。
runntime
runntime表示被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。与compile相比,跳过编译而已,说实话在终端的项目(非开源,企业内部系统)中,和compile区别不是很大。比较常见的如JSR×××的实现,对应的API jar是compile的,具体实现是runtime的,compile只需要知道接口就足够了。oracle jdbc驱动架包就是一个很好的例子,一般scope为runntime。另外runntime的依赖通常和optional搭配使用,optional为true。我可以用A实现,也可以用B实现。
provided
provided意味着打包的时候可以不用包进去,别的设施(Web Container)会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是在打包阶段做了exclude的动作。
system
从参与度来说,也provided相同,不过被依赖项不会从maven仓库抓,而是从本地文件系统拿,一定需要配合systemPath属性使用。
1.2 小结
scope取值 | 编译 | 测试 | 运行 | 依赖传递 | 例子 |
---|---|---|---|---|---|
compile | ✓ | ✓ | ✓ | 是 | spring-core 、log4j |
provided | ✓ | ✓ | 否 | servlet-api |
|
runtime | ✓ | 是 | JDBC-driver |
||
test | ✓ | 否 | junit |
||
system | ✓ | ✓ | 是 | 非Maven 仓库且本地 |
1.3 依赖传递
Maven中依赖包本身具有依赖链关系。例如:项目A—(依赖)–>B.jar
—(依赖)–>C.jar
,当前A项目的pom
文件中引用的B.jar
,并且B.jar
依赖包的scope
的标签属性已知。那么C.jar
在项目A中的依赖怎么确定呢?
初始项目scope值 | compile | provided | runtime | test |
---|---|---|---|---|
compile | compile(*) | - | runtime | - |
provided | provided | - | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |
scope的依赖传递
A–>B–>C。当前项目为A,A依赖于B,B依赖于C。知道B在A项目中的scope,那么怎么知道C在A中的scope呢?答案是:
当C是test或者provided时,C直接被丢弃,A不依赖C;
否则A依赖C,C的scope继承于B的scope。
每个范围(除了import
)以不同的方式影响传递依赖关系,如下表所示。如果将依赖项设置为左列中的范围,则该依赖项的传递依赖项具有跨顶行的范围会导致主项目中的依赖项具有交叉点列出的范围。如果没有列出范围,则意味着省略了依赖项。
第二部分 打包
2.1 打包范围
在Java
项目完成开发后,我们最终会把项目打包成jar
后部署在生产运行。对于项目中依赖的jar包可能在项目运行的classpath
(部署服务器环境)中了。特别常见就是对于大数开发(MapReduce
、Spark
、Flink
)中,很多依赖包已经在客户端lib
目录中了,无需重复将依赖包重复打入应用包中,甚至造成包冲突。
对于不用打包至应用包的依赖包我们配置scope
标签为provided
(即目标服务器已经提供了)。
compile
在开发项目的
classpath
(编译环境)和打包时候都有效。provided
表示该依赖包已经由目标服务器环境(
hadoop
)、容器(如tomcat
的libs
目录)和JDK
提供。只在编译的classpath
中加载和使用,打包的时候不会包含在项目目标包中。runtime
一般是运行和测试环境使用,编译时候不用加入classpath,打包时候会打包到目标包中。一般是通过动态加载或接口反射加载的情况比较多。也就是说程序只使用了接口,具体的时候可能有多个,运行时通过配置文件或jar包扫描动态加载的情况。典型的包括:JDBC驱动等。
test
单元测试场景使用。在编译环境加入
classpath
,但打包时不会加入。system
不会打包至项目目标包。如果需要本地的依赖包,需要将该包注册制本地镜像库。然后再使用
compile
打包至项目目标包。
2.2 小结
对于上面的分析总结成表格如下:
scope标签值 | 打包情况(是否打入项目包) |
---|---|
compile | 是。打包环境有效,依赖包打包至项目包 |
provided | 否。打包环境无效,不会打包至项目包 |
runtime | 是。打包环境有效,依赖包打包至项目包 |
test | 否。打包环境无效,不会打包至项目包 |
system | 否。打包环境无效,不会打包至项目包 |
第三部分 总结
https://segmentfault.com/a/1190000019266080
参考文献及资料
1、Maven官网,链接:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
2、Maven依赖访问,链接:https://www.baeldung.com/maven-dependency-scopes