1. 前提说明
我们知道,用可以引用任何你需要的依赖。那么问题来了,
- maven是怎么引入依赖的 ?
- maven引入依赖后是如何管理的 ?
方便参考,我们引入一段pom.xml文件声明依赖的代码示例:
<dependencys>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<type></type>
<scope></scope>
<optional></optional>
<exclusions>
<exclusion></exclusion>
</exclusions>
</dependency>
</dependencys>
- type | 依赖类型,对应构件中定义的 packaging,可不声明,默认为 jar;
- scope | 依赖范围
- optional | 依赖是否可选
- exclusions | 排除传递依赖
2. 依赖声明
依赖声明三要素坐标 groupId
、artifactId
、version
通过坐标可定位依赖的某个版本的jar包,maven会自动从远程的中央仓库下载到我们本地电脑上,在打包的时候,就会自动使用。
3. 依赖范围
<scope></scope>
Maven有三种classspath
(项目中依赖的各种类)。简单来说,不同的依赖范围,会导致依赖包在编译、测试或者打包运行的时候,有时候可以使用,有时候不能使用对应的依赖。
-
在编译源代码的时候;
-
在编译测试代码以及执行测试代码的时候;
-
实际运行项目的时候;
compile
默认,对编译、测试和运行的classpath都有效。一般都是用这种scope
test
仅对测试代码的时候有效,编译或者运行代码的时候无效。比如junit,一些测试框架,或者只有在测试代码中才会使用的一些依赖,会设置为test,这个的好处在于说,打包的时候这种test scope是不会放到最终的发布包里面,减少发布包的大小。
provided
编译和测试的时候有效,运行的时候无效,因为可能环境已经提供了,比如servlet-api,在运行的时候,servlet容器会提供依赖。servlet-api是用来开发java web项目的,可能你在开发和执行单元测试的时候,需要在pom.xml里面声明这个servlet-api的依赖,因为要写代码和测试代码。但是最终打包之后,放到tomcat容器里面去跑的时候,是不需要将这个servlet-api的依赖包打入发布包中的,因为tomcat容器本身就会给你提供servlet-api的包。
runtime
测试和运行代码的时候有效,编译代码的时候无效,比如jdbc的驱动实现类,比如mysql驱动。因为写代码的时候是基于javax.sql包下的标准接口去写代码的。然后在测试的时候需要用这个包,在实际运行的时候才需要用这个包的,在编译的时候只要javax.sql接口就可以了,不需要mysql驱动类。
当然,一般我们声明mysql驱动的时候,不会设置为runtime,因为也许你在开发代码的时候会用到mysql驱动特定的api接口,不仅仅只是用javax.sql。
总结,如下表所示:
compile | test | provided | runtime | |
---|---|---|---|---|
编译 | ✅ | ✅ | ✅ | ❎ |
测试 | ✅ | ✅ | ✅ | ✅ |
运行 | ✅ | ❎ | ❎ | ✅ |
4. 依赖传递
如下表,所示展示传递性依赖机制对依赖范围的影响,第一列:一级依赖 | 第一行:二级依赖
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | runtime | ||
test | test | test | ||
provided | provided | provided | provided | |
runtime | runtime | runtime |
在早期,我们在做java项目的时候,基本都是纯手动直接放一大堆jar包。然后尝试运行,发现报错,根据报错的提示内容,比如缺失某某类。然后我们再去找对应jar包。继续执行又报错,提示说缺失某某类,每个依赖可能有其他的依赖,而其他依赖又有别的依赖,循环往复,极其麻烦,仅仅配置好jar包运行初期项目就让人心力憔悴了。
maven的出现让我们极大的摆脱了手工干预,让我们能够腾出时间去专心开发。我们说说Maven的传递性依赖,就是说它会自动帮我们递归解析所有的依赖,然后负责将依赖下载下来,接着所有层级的依赖,都会成为我们的项目的依赖,不需要我们手工干预。所有需要的依赖全部下载下来,不管有多少层级。
比如,我们依赖于A,A是compile;A依赖于B,B是test;我们对B的依赖范围是空,就是我们不会去依赖B,因为你自己想想都知道,A只有在测试的时候才会使用B。我们依赖A是生产用的,我们去依赖B干嘛?B是给A测试的。
5. 依赖调解
既然说maven会自动解析所有层级的依赖,给我们自动下载所有的依赖,但是可能会出现依赖冲突的问题,
maven如何解决依赖冲突?
- 比如A->B->C->X(1.0),A->D->X(2.0),A有两个传递性依赖X,不同的版本,就产生了依赖冲突的问题。
根据依赖调解机制,我们采用就近原则,离A最近的A就依赖谁(X1.0)。
- 再比如,如果A->B->X(1.0)和A->D->X(2.0),路径等长呢?
根据依赖调解机制,我们采用第一声明原则,就是哪个依赖在pom.xml里先声明,就用哪个!
6. 可选依赖
<optional>true</optional>
此时依赖传递失效,不会向上传递
怎么理解?
比如:如果A依赖于B,B依赖于C,B对C的依赖是optional,那么A就不会依赖于C。反之,如果没有optional,根据传递性依赖机制,A会依赖于C。