maven的依赖管理

一、依赖管理

Maven 一个核心的特性就是依赖管理。当我们处理多模块的项目(包含成百上千个模块或者子项目),模块间的依赖关系就变得非常复杂,管理也变得很困难。针对此种情形,Maven 提供了一种高度控制的方法。

1、依赖调节

1.1 第一声明优先原则

在pom.xml文件中引入两个模块 AOP 和 Messaging 的架包坐标信息,先声明的是AOP模块,当AOP和Messaging的依赖包发生冲突时,项目只会引入AOP模块底下的冲突依赖包,Messaging的冲突依赖包则会被排除。

1.2 路径近者优先原则

这个就比较好理解了,就是直接依赖优先于传递依赖,当然会存在多层依赖,当多层依赖发生冲突时,maven的依赖管理会优先引入依赖层级最少的冲突依赖。越靠近项目的依赖,越被优先使用

1.3 后者覆盖前者

如果相同的pom文件中,声明了同一个jar包,后声明的会覆盖前面的

二、依赖传递

依赖范围不仅可以控制依赖和三种classpath的关系,还对传递性依赖产生影响。

假设A依赖B, B依赖C, 我们说A对于B是第一直接依赖, B对C是第二直接依赖, A对于C是传递性依赖。 第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。 如下图: 最左边一列表示第一直接依赖方位, 最上面一行表示第二直接依赖范围, 中间交叉单元格则表示传递性依赖的范围。

compile test provided runtime
compile compile runtime
test test test
provided provided provided provided
runtime runtime runtime

通过上表可以发现规律:当第二直接依赖的范围是compile的时候,传递性依赖与第一直接依赖的范围一致; 当第二直接依赖的范围是test的时候,依赖不会得以传递;当第二直接依赖是provided的时候,值传递第一直接依赖范围也为provided的依赖,且传递性依赖范围同样为provided; 当第二依赖的范围是runtime的时候,传递性范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。

三、可选依赖(optional)

准备两个工程,简单点,就是A和B

只看POM文件,这是A的pom文件。

version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>A</groupId>
<artifactId>A</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
</dependencies>
</project>

这是B的pom文件:

version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>B</groupId>
<artifactId>B</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>A</groupId>
<artifactId>A</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

在这种情况下,joda-time包在B工程会被正常引用。

加入optional

在A工程对joda-time添加optional选项,这时在B工程中,joda-time依赖包会消失.

1
2
3
4
5
6
7
    <dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
<optional>true</optional>
</dependency>
</dependencies>

parent 继承的情况

如果A的pom像下面这样配置

version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>A</groupId>
<artifactId>A</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
</project>

B再去引用的话,还是可以正常引用joda-time包,optional选项在统一控制版本的情况下会失效

四、排除依赖

排除依赖顾名思义就是将不需要的依赖排除,这也是依赖冲突的一种解决方案。

如,项目A依赖于B,B依赖于C,项目A依赖于D,D依赖于C,如果在项目A的pom.xml中先定义B,根据maven自调节的第一声明优先原则,那么D依赖的C就会被默认排除。此时,如果使用关键字exclusion在B中排除C,那么C自然不会再发生冲突了,因为B依赖的C被排除了,项目A会引入D依赖的C。

1
2
3
4
5
6
7
<exclusions>
<exclusion>
<groupId>###</groupId>
<artifactId>###</artifactId>
</exclusion>
</exclusions>

五、依赖范围(scope)

1
<scope>compile</scope>
5.1 Compile struts2-core

编译(compile)时需要 测试时需要,,运行时需要,打包时需要

5.2 Provided jsp-api.jar servlet-api.jar

编译(compile)时需要,测试(test)时也需要 ,运行时不需要,打包时不需要

5.3 Runtime 数据库驱动包

编译时不需要,测试时需要,运行时需要,打包时需要

5.4 Test junit.jar

编译时不需要,测试时需要,运行时不需要,打包也不需要

通过下表可以知道依赖范围表示的作用域。

依赖范围 对于编译执行环境有效 对于测试执行环境有效 对于运行时执行环境有效 打 包 例 子
compile spring-core
test × × × junit
provided × × servlet-api
runtime × JDBC驱动
system × 本地的,Maven仓库之外的类库