一、什么是依赖
每当我们需要使用某个框架时,比如 SpringMVC,那么我们需要导入相应的 jar 包,但是手动导入包的时候,往往会漏掉几个 jar 包,那么在使用该框架的时候系统就会报错。那么我们就说导入的包与未导入的包存在依赖关系。而使用 Maven 我们只需要在 pom.xml 文件中进行相应的配置,它就会帮助我们自动管理 jar 包之间的依赖关系。
二、依赖的详细配置
我们以 Junit 为例,在 pom.xml 文件中进行详细而完整的配置。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
- dependencies:一个 pom.xml 文件中只能存在一个这样的标签。用来管理依赖的总标签。
- dependency:包含在dependencies标签中,可以有无数个,每一个表示一个依赖
- groupId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。
- type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar。
- scope:依赖的范围,默认值是 compile。后面会进行详解。
- optional:标记依赖是否可选。
- exclusions:用来排除传递性依赖,后面会进行详细介绍。
三、依赖的范围
一般情况下,我们对前面三个依赖用的比较多。
- 主程序目录结构 src/main/java
- 测试程序目录结构:src/test/java
1、compile 范围依赖
- 对主程序是否有效:有效
- 对测试程序是否有效:有效
- 是否参与打包:参与
- 是否参与部署:参与
- 典型例子:log4j
2、test 范围依赖
- 对主程序是否有效:无效
- 对测试程序是否有效:有效
- 是否参与打包:不参与
- 是否参与部署:不参与
- 典型例子:Junit
3、provided 范围依赖
- 对主程序是否有效:有效
- 对测试程序是否有效:有效
- 是否参与打包:不参与
- 是否参与部署:不参与
典型例子:servlet-api.jar,一般在发布到服务器中,比如 tomcat,服务器会自带servlet-api.jar 包,所以provided 范围依赖只在编译测试有效。
4、runtime 范围依赖
在测试、运行的时候依赖,在编译的时候不依赖。例如:JDBC驱动,项目代码只需要jdk提供的jdbc接口,只有在执行测试和运行项目的时候才需要实现jdbc的功能。
四、依赖的传递
比如我们创建三个Maven工程,maven-first、maven-second以及maven-third,而third依赖于second,second又依赖于first,那么我们说 second 是 third 的第一直接依赖,first是second的第二直接依赖。而first是third的间接依赖。
依赖之间的传递如下图:第一列表示第一直接依赖,第一行表示第二直接依赖
当第二依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
当第二直接依赖的范围是test的时候,依赖不会得以传递。
当第二依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为 provided;
当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递的依赖范围为runtime;
例如:创建3个maven工程。
在first-maven的pom.xml中配置
然后second-maven依赖fisrt-maven,third-maven依赖second-maven,其pom.xml 文件如下:
second-maven的 pom.xml
third-maven的 pom.xml
我们发现在 third-maven和 second-maven都没有 first-maven引入的 Junit 包,正好符合上面总结的第二点:当第二直接依赖的范围是test的时候,依赖不会得以传递。
第二依赖范围是compile。如果我们将 first-maven的Junit 改为 compile,那么将会符合上面总结的第一点:当第二依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
五、依赖的排除
如果我们在当前工程中引入了一个依赖是 A,而 A 又依赖了 B,那么 Maven 会自动将 A 依赖的 B 引入当前工程,但是个别情况下 B 有可能是一个不稳定版,或对当前工程有不良影响。这时我们可以在引入 A 的时候将 B 排除。
比如:我们在first-maven中添加 spring-core,maven 会自动将 commons-logging添加进来,那么由于 second-maven是依赖 first-maven 的,那么 second-maven 中将存在 spring-core(自带了commons-logging),这时候我们不想要 commons-logging,那该怎么办呢?我们上面第二大点提到了:
如何排除呢?我们在 second-maven 的 pom.xml 文件中添加如下代码:
六、依赖的冲突
在maven中存在两种冲突方式:一种是跨pom文件的冲突,另一种是同一个pom文件中的冲突。
跨 pom 文件,路径最短者优先。比如我们在 first-maven中的 Junit 是4.12版本的,second-maven中的 Junit 是4.10版本的,那么third-maven 中的 Junit 将会是那个版本呢?
由上图我们可以看出,由于 second-maven 是 third-maven的直接依赖,明显相比于 first-maven路径要短,所以 third-maven的 Junit 版本与 second-maven 保持一致。
同一个pom.xml 文件,先申明者优先。
七、可选依赖
Optional标签标示该依赖是否可选,默认是false。可以理解为,如果为true,则表示该依赖不会传递下去,如果为false,则会传递下去。
我们是在 second-maven的 pom 文件中设定 Junit 不可传递,那么 third-maven工程中将不会有来自 second-maven 的 Junit 的传递。