maven快速入门第十一讲——依赖的传递性
在《maven快速入门第六讲——依赖的作用范围》这一讲中,我详细介绍了依赖的作用范围。而在本讲中,我会介绍依赖的传递性,你听的没错,依赖是具有传递性的。但是,我们也要明白工程与工程之间也是有依赖关系的,正如下图所示。
依赖具有传递性
在前一讲中,我们使用maven分模块构建了一个聚合工程。接下来,我就要在该聚合工程的基础上编写代码了。
首先,在crm-dao子工程中创建相应的接口与实现类。
-
接口(比如说ICustomerDao.java)
package com.meimeixia.crm.dao; public interface ICustomerDao { }
-
以上接口的一个实现类(例如CustomerDao.java)
package com.meimeixia.crm.dao.impl; import com.meimeixia.crm.dao.ICustomerDao; public class CustomerDao implements ICustomerDao { }
然后,同样在crm-service子工程中创建相应的接口与实现类。
-
接口(比如说ICustomerService.java)
package com.meimeixia.crm.service; public interface ICustomerService { }
-
以上接口的一个实现类(例如CustomerService.java)
package com.meimeixia.crm.service.impl; import com.meimeixia.crm.service.ICustomerService; public class CustomerService implements ICustomerService { private ICustomerDao customerDao; public void setCustomerDao(ICustomerDao customerDao) { this.customerDao = customerDao; } }
此时,你会发现以上CustomerService实现类编写好之后,报错了。
eclipse提示我们要在crm-service子工程中创建一个ICustomerDao接口,这显然不能够啊!因为这里是要导入crm-dao子工程中的ICustomerDao接口。我们的crm-service子工程将来要把数据存储到数据库中,必然需要依赖crm-dao子工程,所以需要在crm-service子工程的pom.xml文件中添加crm-dao子工程的依赖,即添加crm-dao子工程的工程坐标。大家可以按照下图所示的步骤来进行操作。
点击OK按钮,再保存一下pom.xml文件,这时你会发现pom.xml文件变成了下面这个样子。
从中可以清楚地看到添加了一个dependencies节点,该节点中的内容就是crm-dao子工程的工程坐标。而且再仔细查看一下crm-service子工程的结构,你会发现它引入了crm-dao子工程。
这时,便可以在CustomerService实现类导入crm-dao子工程中的ICustomerDao接口了。
同理,crm-web子工程需要调用业务,所以就需要在其中添加对crm-service子工程的依赖,即需要在crm-web子工程的pom.xml文件中添加crm-service子工程的工程坐标。
仔细查看crm-web子工程的结构,此时可以看到,它不仅把crm-service子工程依赖进来了,而且还把crm-dao子工程也依赖进来了,这是因为依赖具有传递性。
如果MakeFriends.jar直接依赖于HelloFriend.jar,而HelloFriend.jar又直接依赖于Hello.jar,那么MakeFriends.jar也依赖于Hello.jar,这就是传递性依赖,只不过这种依赖是间接依赖。
还记得在web项目整合Struts2框架时,在web项目的pom.xml文件中只添加了一个struts2-core的依赖,结果发现所有与Struts2开发相关的jar包都引入到该web项目中了吗?
因为我们的项目依赖struts2-core-2.3.24.jar,而struts2-core-2.3.24.jar又会依赖xwork-core-2.3.24.jar、commons-lang3-3.2.jar等等,所以xwork-core-2.3.24.jar这些jar包也出现在了我们的maven工程中,这种现象我们称之为依赖传递。
你不仅想问了,我是怎么知道struts2-core-2.3.24.jar又会依赖xwork-core-2.3.24.jar、commons-lang3-3.2.jar这些jar包的呢?在本地仓库中找到struts2-core-2.3.24.jar所在的位置,然后你就能看到struts2-core-2.3.24.pom这样一个文件了,它实际上是一个pom.xml文件。
打开struts2-core-2.3.24.pom文件,你就能看到struts2-core-2.3.24.jar所依赖的那些jar包了,如下图所示。
其实,也可以借助eclipse来查看struts2-core-2.3.24.jar依赖的层级关系。大家可以按照下图所示的步骤来进行操作。
使用<exclusion>
标签排除jar包冲突
在crm-parent父工程中添加struts2-core和hibernate-core的依赖,如下图所示。
这时,你会发现有jar包冲突。
如何解决javassist这个jar包存在版本上的冲突问题呢?可以使用<exclusion>
标签来排除jar包冲突问题,大家可以按照下图所示的步骤来进行操作。
然后,会弹出如下一个窗口,点击其中的OK按钮即可,顺便再保存一下pom.xml文件。
这时你会发现pom.xml文件变成了下面这个样子。
依赖的传递范围
如果crm-dao子工程开发完毕了,那么紧接着就要编写JUnit单元测试用例了。相应地,就要在pom.xml文件中添加JUnit的依赖了,并且scope要置为test。
仔细查看crm-dao子工程的结构,发现junit-4.9.jar这个jar包确实引入了,这是毋庸置疑的。
这时,我们可以晓得crm-service子工程直接依赖于crm-dao子工程,而crm-dao子工程又直接依赖于junit-4.9.jar,由于依赖具有传递性,那么想必crm-service子工程也会依赖于junit-4.9.jar。也就是说,crm-service子工程中也会引入junit-4.9.jar这个jar包。真的是这样吗?发现并不是这样,仔细查看crm-service子工程的结构,你就知道该工程中并没有引入junit-4.9.jar这个jar包。
卧槽!这与我们所知道的依赖具有传递性不一样啊!这到底是咋一回事啊?其实依赖传递也是有范围的,传递依赖不能无休止的往下面传递下去。
以上表格中的中间部分即是传递依赖的范围,可见传递依赖不是无休止的往下面传递下去的。
如果我们现在将crm-dao子工程pom.xml文件中的JUnit的依赖修改成下面这个样子,
那么,不仅crm-dao子工程会引入junit-4.9.jar这个jar包,而且crm-service子工程也会引入这个jar包。
总结
有同学可能不记得上表中所列出的传递依赖的范围,不记得一点关系都没有,谁的记性都不会那么好。一旦当我们发现项目中需要的某一个依赖(即jar包)没有传递过来时,该怎么办啊?真的是没必要照着上表来修改,你自己在工程中添加对应的依赖就可以了。
总之一句话:如果在依赖传递过程中,导致jar包丢失,我们的做法很简单,就是再导入一次坐标。
依赖传递的两个原则
依赖传递有两个原则,一个是第一声明者优先原则,另一个是路径近者优先原则。下面我会对这两个原则进行详细阐述。
第一声明者优先原则
在Struts2框架与Spring框架进行整合的时候,我们需要导入struts2-spring-plugin-2.3.24.jar这样一个jar包,所以还得在父工程的pom.xml文件中添加该jar包的坐标。
<!-- 下面这个会引入spring-beans-3.0.5.RELEASE.jar -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.24</version>
</dependency>
大家可以按照下图所示的步骤来查看struts2-spring-plugin-2.3.24.jar依赖的层级关系。
然后,导入与Spring框架开发相关的jar包。例如我故意在父工程的pom.xml文件中添加spring-context-4.2.2.RELEASE.jar的坐标,为什么说故意呢?因为在这儿我仅仅只是用它来说明依赖传递的第一个原则,即第一声明者优先原则。而在实际开发中,本人使用的是spring-framework-4.2.4.RELEASE这个版本的Spring。
<!-- 下面这个会引入spring-beans-3.0.5.RELEASE.jar,但是最后生效的只有这个spring-beans-3.0.5.RELEASE版本的jar包 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.24</version>
</dependency>
<!-- 下面这个会引入spring-beans-4.2.2.RELEASE.jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
照理来说,在各个工程中应该引入spring-beans-3.0.5.RELEASE.jar和spring-beans-4.2.2.RELEASE.jar这两个jar包,结果发现生效的还是通过struts2-spring-plugin-2.3.24.jar依赖进来的版本,即spring-beans-3.0.5.RELEASE这个版本的jar包。
如果将父工程pom.xml文件中的struts2-spring-plugin-2.3.24.jar与spring-context-4.2.2.RELEASE.jar这俩jar包的坐标颠倒一下位置,那么结果会是什么呢?
<!-- 依赖管理的第一个原则:第一声明者优先原则(谁先声明,谁先用) -->
<!-- 下面这个会引入spring-beans-4.2.2.RELEASE.jar,但是最后生效的只有这个spring-beans-4.2.2.RELEASE版本的jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
<!-- 下面这个会引入spring-beans-3.0.5.RELEASE.jar -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.24</version>
</dependency>
结果发现此时生效的是通过spring-context-4.2.2.RELEASE.jar依赖进来的版本了,即spring-beans-4.2.2.RELEASE这个版本的jar包。
由于spring-context-4.2.2.RELEASE.jar所依赖的spring-beans-4.2.2.RELEASE.jar在前边,所以以它所依赖的spring-beans-4.2.2.RELEASE.jar为准,最终spring-beans-4.2.2.RELEASE这个版本的jar包便添加到了工程中了。这就是所谓的第一声明者优先原则。
路径近者优先原则
struts2-spring-plugin-2.3.24.jar跟spring-context-4.2.2.RELEASE.jar都会添加spring-beans的依赖,只不过是struts2-spring-plugin-2.3.24.jar会引入spring-beans-3.0.5.RELEASE这个版本的jar包,spring-context-4.2.2.RELEASE.jar会引入spring-beans-4.2.2.RELEASE这个版本的jar包。如果这时我们直接加入新的依赖,即spring-beans-4.2.4.RELEASE.jar的坐标,结果会是什么呢?
<!-- 下面这个会引入spring-beans-4.2.2.RELEASE.jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
<!-- 下面这个会引入spring-beans-3.0.5.RELEASE.jar -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.24</version>
</dependency>
<!-- 依赖管理的第二个原则:路径近者优先原则 -->
<!-- 这个spring-beans-4.2.4.RELEASE版本的jar包放到最后面去了,但是它还是生效了。那这是为啥啊? -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
结果发现生效的是直接引入的版本,即spring-beans-4.2.4.RELEASE这个版本的jar包。
此时,工程中如果要引入spring-beans,可以有以下方法:
- 通过引入struts2-spring-plugin-2.3.24.jar,它会引入spring-beans-.3.0.5.RELEASE这个版本的jar包,但需要经过的路径为2个节点;
- 通过引入spring-context-4.2.2.RELEASE.jar,它会引入spring-beans-.4.2.2.RELEASE这个版本的jar包,但需要经过的路径也为2个节点;
- 如果直接引入spring-beans-4.2.4.RELEASE.jar,那么只需要经过1个节点。
因此不管spring-beans-4.2.4.RELEASE.jar的位置在哪,工程中始终都是引入spring-beans-4.2.4.RELEASE这个版本的jar包。这就是所谓的路径近者优先原则。
上一篇: 图像质量评价指标之Matlab实现
下一篇: markdown基础语法总结