Maven 依赖范围和依赖传递详解
18.10.8 松江图书馆
依赖基本配置
在pom.xml文件 根元素project下的 dependencies标签中,配置依赖信息,内可以包含多个 dependence元素,以声明多个依赖。每个依赖dependence标签都应该包含以下元素:
- groupId, artifactId, version : 依赖的基本坐标, 对于任何一个依赖来说,基本坐标是最重要的, Maven根据坐标才能找到需要的依赖。
这3个属性,可以理解成一个3维的x-y-z坐标,可以通过3个值,来确定一个库中维一个依赖
gooupId 可以理解成一个java的包名(java包名中默认以公司域名倒写),在对应的.m2仓库,它是一个可以有很多层的目录,以. 来进行目录分级
artifactID 可以理解是项目或模块的名称
version 表示不同的版本号
-
type: 依赖的类型,大部分情况下不需要声明。 默认值为jar
-
Scope: 依赖范围(compile,test,provided,runtime,system 五种状态)
-
Optional:标记依赖是否可选
-
exclusions: 用来排除传递性依赖 其中可配置多个exclusion标签,每个exclusion标签里面对应的有groupId, artifactId, version三项基本元素
example Code:
<project>
<groupId>groupA</groupId>
<artifactId>artifactA</artifactId>
<version>1.0</version>
<type>jar<type>
<scope>complie<scope>
<dependencies>
<dependency>
<groupId>groupC</groupId>
<artifactId>artifactC</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>groupD</groupId>
<artifactId>artifactD</artifactId>
<version>1.0</version>
</dependency>
<exclusions>
<exclusion>
<groupId>T</groupId>
<artifactId>jartifactD</artifactId>
</exclusion>
<exclusion>
<groupId>TS</groupId>
<artifactId>asm</artifactId>
</exclusion>
</exclusions>
</dependencies>
</project>
依赖范围
-
compile: 编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效
-
system: 系统依赖范围。该依赖与编译、测试、运行三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。
example code:
<dependencies>
<dependency>
<groupId>sun.jdk</groupId>
<artifactId>tools</artifactId>
<version>1.5.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
-
provided: 已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍(如:servlet-api)。
-
runtime: 运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
-
test: 测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。
典型的例子就是JUnit
,它只有在编译测试代码及运行测试的时候才需要。
依赖传递
maven默认依赖体制
maven引入的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖。但有时候,当传递性依赖造成问题的时候,我们就需要清楚地知道该传递性依赖是从哪条依赖路径引入的。
maven对于依赖相同的资源,默认会做出以下优化:
第一原则:以短路径长度为准
例如,项目A有这样的依赖关系 : A–>B–>C–>X(1.0)、A–>D–>X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版 本的X,那么哪个X会被maven解析使用呢?两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择一个。maven依赖调解的第一原则:路径最近者优先。该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。
第二原则:相同路径长度时,以pom中声明顺序先者为准
依赖调解第一原则不能解决所有问题,比如这样的依赖关系:A–>B–>Y(1.0),A–>C–>Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度是一样的,都为2。那么到底谁会被解析使用呢?在maven2.0.8及之前的版本中,这是不确定的,但是maven2.0.9开始,为了尽可能避免构建的不确定性,maven定义了依赖调解的第二原则:第一声明者优先。在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用。顺序最靠前的那个依赖优胜。
2020.1.14 修改
关于第二条原则,有些歧义对于在同一个pom文件中,如果引入同一jar包,但不同版本时,后者会覆盖前者,则以后者为准. (当然这种情况一般不会出现)
另外,还有一种解决冲突的办法: 版本锁定原则.
版本锁定原则
面对众多的依赖,版本锁定这一种方式不需要考虑依赖的路径、声明优化等因素,可以直接锁定所依赖jar包的版本,锁定后不会考虑声明顺序及路径。
主要是使用dependencyManageme’标签来是实现
下面以锁定Struts2、Spring、Hibernate版本为例:
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<struts.version>2.3.24</struts.version>
</properties>
<!-- 锁定版本,struts2-2.3.24、spring4.2.4、hibernate5.0.7 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
注意:在工程中锁定依赖的版本并不代表在工程中添加了依赖, 如果工程需要添加锁定版本的依赖则需要单独添加标签,依然需要使用dependencies标签来引入依赖,只是不必再指定版本号.
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts.version}</version>
</dependency>
</dependencise>
maven pom中配置 exclusion标签 手动去除依赖
场景: 比如,A 和 B 使用需都需要引用c jar包,A引用c的版本为1.0,B引用C的版本为2.0
原则上,我们使用最新版本作为支持,按照maven依赖系统默认原则,其实会引用的是C的1.0版本,此时我可以用exclusion来排除依赖
<dependencies>
<dependency>
<groupId>A</groupId>
<artifactId>A</artifactId>
<version>xxx</version>
<exclusions>
<exclusion>
<groupId>C</groupId>
<artifactId>C</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>B</groupId>
<artifactId>B</artifactId>
</dependency>
</dependencies>
dependencyManagement和dependencise的区别
dependencyManagement标签和denpendencise标签和可作为同级标签,也可作为上下级标签;
同级:dependencyManagement标签功能在于“申明”依赖,denpendencise标签在于“申明和使用”依赖
上下级: 因为dependencyManagement 申明一个依赖需要借助denpendencise标签,此时denpendencise标签为子标签
关于“申明”和“申明和使用”依赖的解释:
dependencyManagement标签申明依赖,主要功能为是版本version控制;
尤其在多模构建项目时,在统一的父模块中(父模块配置为<packaging>pom</packaging>),通过dependencyManagement标签,申明依赖,确认了依赖的版本version信息,子模块只需要通过denpendencise标签来引入依赖,不用指定版本,默认用的就是父类版本;
此时要注意: 子项目依赖父类没有申明的依赖,一定要加版本号,此时子类会仓库自行找对应的依赖信息;子项目依赖父类申明的依赖,version版本和父类不同,此时子类也会在仓库中找对应自己的依赖信息;

本文详细讲解了Maven的依赖配置,包括依赖范围(compile, test, provided, runtime, system)及其作用,依赖传递的默认机制,以及如何通过exclusions标签排除传递性依赖。同时介绍了dependencyManagement和dependencies的区别,帮助理解Maven如何管理与解决依赖冲突。"
113671210,10543778,Python gensim实现文档相似性,"['自然语言处理', '文本分析', '信息检索', 'Python库']
746

被折叠的 条评论
为什么被折叠?



