依赖的配置
一个依赖声明可以包含如下元素
<project>
...
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
...
</exclusion>
...
</exclusions>
</dependency>
...
</dependencies>
...
</project>
根元素的project
下的dependencies
可以包含一个或者多个dependency
元素,以声明一个或者多个项目依赖。每个项目可以包含的元素有:
-
groupId
、artifactId
和version
依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的、Maven根据坐标才能找到所需要的依赖 -
type
依赖的类型,大部分情况下不必设置,默认值为jar -
scope
依赖的范围 -
optional
标记依赖是否可选 -
exclusions
用来排除传递性依赖
依赖范围
首先需要知道Maven对于编译、测试、运行使用三套不同的classpath
,Maven依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系
-
compile
编译依赖范围。默认依赖范围。对于编译、测试、运行三种classpath都有效 -
test
测试依赖范围。只对测试classpath有效,典型的例子是JUnit,它只有在编译测试代码及运行测试的时候才需要 -
provided
已提供依赖范围。对于编译和测试classpath都有效,典型的就是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要maven重复地引用一遍 -
runtime
运行时依赖范围。对于测试和运行classpath都有效,典型例子就是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动 -
system
系统依赖范围。对于编译和测试classpath都有效,需要与systemPath
配合使用指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此谨慎使用 -
import
(Maven 2.0.9及以上)
导入依赖范围
依赖范围scope
|
对于编译classpath有效 | 对于测试classpath有效 | 对于运行classpath有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | - | Y | - | Junit |
provided | Y | Y | - | servlet-api |
runtime | - | Y | Y | JDBC驱动实现 |
system | Y | Y | - | 本地的,Maven仓库之外的类库文件 |
传递性依赖
何为传递性依赖
如下图,假设A模块与B模块的依赖范围都为默认compile,那么C模块就会成为A模块的compile范围依赖,C模块是A模块的一个传递性依赖
有了传递性依赖机制,就不需要考虑一个模块所依赖了什么,也不用担心引用多余的依赖。Maven会解析各个直接依赖的POM,将那些必要的间接依赖以传递性依赖的形式引入到当前的项目中
传递性依赖和依赖范围
依赖范围不仅可以控制依赖与三种classpath的关系,还对传递性依赖产生影响
如下图,A依赖于B并且称为第一依赖,B依赖于C并且称为第二依赖,A对于C是传递性依赖由第一依赖第二依赖的依赖范围决定
如下表所示,左边一列为第一依赖,最上面一行为第二依赖,中间的交叉单元格则表示传递性依赖
第一依赖\第二依赖 | compile | test | provided | runtime |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
依赖调解
当传递性依赖造成问题的时候,我们需要清楚地知道该传递性依赖是从那条依赖路径引入的
Maven依赖调解(Dependency Mediation)共有两条原则
- 路径最近者优先
如,A项目存在如下依赖:
A -> B -> C -> X(1.0)
和A -> D -> X(2.0)
X(1.0)的路径为3,而X(2.0)的路径为2,因此X(2.0)会被解析使用 - 第一声明者优先
如,A项目存在如下依赖:
A -> B -> Y(1.0)
和A -> C -> Y(2.0)
依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,如B的依赖声明在C之前,那么Y(1.0)就会被解析使用
可选依赖-optional
- 为什么?
比如项目B实现了两个特性,其中的特性一依赖于X,特性二依赖于Y,而且这两个项目是互斥的,用户不可能同时拥有两个特性,比如B是持久化层隔离工具包,在构建时需要两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库 -
依赖关系
如图,如果这三个依赖的范围都是compile,那么X、Y就是A的compile范围传递性依赖。然而由于X、Y为可选依赖,依赖并不会得到传递
注
:在理想情况下,是不应该使用可选依赖的。使用可选依赖的原因是某个项目实现了多个特性,在面向对象程序设计中,有个单一职责性原则,意指一个类应该只有一项职责,而不是揉合太多的功能
网友评论