Maven实战07_依赖
1:依赖声明
<project>
...
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
...
</exclusion>
...
</exclusions>
</dependency>
...
</dependencies>
...
</project>
解释说明:
- 根元素prject下的dependencies可以包含一个或所个dependency元素,以声明一个或多个项目依赖
- groupId、artifactId、version:依赖的基本坐标,每一个依赖必须具备的属性,Maven只有根据这些坐标才能找到并下载依赖
- type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,不必声明,默认为jar
- scope:依赖的范围,见下面2:依赖范围
- optional:标记依赖是否可选,见下面4:依赖可选
- exclusion:用来排除传递性依赖,见下面3:传递性依赖
2:依赖范围(用于确认是否可导入依赖包)
Maven在编译项目主代码的时候需要使用一套classpath。例如,邮件模块中的javax.mai依赖会出现
Maven在编译和执行测试的时候会使用另一套classpath。例如,Juni依赖t只会出现在测试classpath下,javax.mail也会出现
Maven在运行实际项目的时候,又会使用一套classspath。例如,javax.mail需要出现,junit则不需要出现。
依赖范围就是用来控制依赖与这三种classpath的(编译classpath、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:
- compile:编译依赖范围。如果没有指定,默认的依赖范围。此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。
- test:测试依赖范围,此依赖范围的Maven依赖,只对于测试的classpath有效,在项目编译主代码或者运行项目的使用时将无法使用此类依赖。
- provided:已提供依赖范围,此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效,例如SpringBoot生成war包,其中的tomcat依赖就要是provided依赖范围,在开发中,就要改成compile依赖
- runtime:运行时依赖范围,此依赖范围的Maven依赖,对于测试和运行有效,但在编译主代码时无效,典型的例子就是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口。
- system:系统依赖范围,该依赖与三种classpath的关系,和provided依赖范围完全一致。但是使用system范围的依赖必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此要谨慎使用。systemPath可以引用环境变量,如:
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
- import(Maven2.0.9及以上):导入依赖范围,该依赖范围不会对三种classpath产生实际的影响。
上述出import以外的各种依赖范围与三种classpath的关系表:
3:传递性依赖
一个基于SpringFramework的项目,如果不使用Maven,就要手动的去添加依赖包,通常是去官网下载,但是里面的依赖很多,会有不必要的依赖存在。另一种做法就是下载必要依赖包,但是这个包中不包含其他相关依赖,到实际使用的时候,根据报错引入相关依赖,这种做法是很麻烦的。
Maven的传递性依赖机制可以解决这个问题,例如:有一个spring-core的依赖,而在spring-core中也存在自己的依赖,包含了一个commons-logging依赖,见代码清单:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
</dependency>
该依赖的依赖范围是compile。而spring-core的依赖范围也是cmpile,因此就会出现传递性依赖,使得项目也同时依赖commons-logging,如图所示
4:传递性依赖和依赖范围
针对上图的传递性依赖做一些解释
Maven project 依赖于 spring-core : 第一直接依赖
spring-core 依赖于 commons-logging :第二直接依赖
Maven project 对于 commons-logging:传递依赖
5:依赖调解
并不是所有的传递性依赖都能正常工作,当传递性依赖造成问题的时候,我们就需要清楚的知道该传递性依赖是从哪条依赖路径引入的。
例如:
- 第一原则:路径最近者优先:
在项目A中存在这样的依赖。A –> B –> C –>X(1.0)、A –> D –> X(2.0),X是A的传递性依赖,但是这两条依赖路径上有两个版本的X,那么哪个X会被Maven解析使用呢?在Maven中采用路径最近者优先策略,因此,X(2.0)会被解析使用
- 第二原则:第一声明者优先
在针对第一原则下路劲相同,版本不一致的缺陷下提出了第二原则,在项目A中存在这样的依赖关系。A –> B –> Y(1.0)、A –> C – > Y(1.0),Y的依赖路径都是2,从Maven2.0.9开始,为了避免构建对的不确定性,Maven使用了该原则,在依赖路径相等的情况下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优胜,如果B的依赖声明在C之前,那么Y(1.0)就会被解析使用。
6:可选依赖
假设有这样一种依赖关系,项目A依赖于项目B,项目B依赖于项目X和项目Y,B对于X和Y的依赖都是可选依赖(optional为true):A -> B,B -> X(可选),B -> Y(可选)。根据传递性依赖的定义,如果所有这三个依赖的范围都是compile,那么X,Y就是A的compile范围传递性依赖。然而,由于这里X,Y是可选依赖,依赖就不会传递,也就是说,X,Y将不会对A有任何影响 。
为什么要使用可选依赖这一特性呢?可能B项目实现了两个特性,其中的一个特性一依赖于X,特性二依赖于Y,而且这两个特性是相互排斥的,用户不可能同时使用两个特性,比如B是一个持久层隔离工具包,它支持多种数据库,包括Mysql,PostgreSQL等,在构建这个工具包时,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。
在理想情况下,是不应该使用可选依赖的,因为使用了可选依赖的原因是某一个项目实现了多个特性,在面向对象设计中,有个单一职责性原则,意指一个类应该只有一个职责,而不是糅合太多的功能。这个原则在规划Maven的时候同样适用。
7:最佳实践
- 排除依赖
之所以要排除依赖的原因有以下几点:
- 依赖不稳定,需要正式版本,多为引用第三方依赖中包含这个不稳定依赖导致
- 由于版权,依赖不再中央仓库,需要排除改依赖
上述图排除依赖的代码清单
<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>xx.xxx.xx</groupId> <artifactId>A</artifactId> <version>1.0-SNAPSHOT</version> <dependency> <groupId>xx.xxx.xx</groupId> <artifactId>B</artifactId> <version>1.0</version> <!-- 排除C的不稳定依赖 --> <exclusions> <exclusion> <groupId>xx.xx.xx</groupId> <artifactId>C</artifactId> </exclusion> </exclusions> </dependency> <!-- 引入C的稳定依赖 --> <dependency> <groupId>xx.xx.xx</groupId> <artifactId>C</artifactId> <version>1.0</version> </dependency> </project>
代码清单说明
上述代码中,项目A依赖于项目B,但是由于一些原因,不想引入项目B中的依赖C,而是自己显示的声明对于项目C稳定版本1.0的依赖。所以在项目B中使用exclusions对依赖进行排除,由英语语法的单复数可知,在exclusions中可包含多个exclusion,即可排除多个依赖。需要注意的是,声明exclusion的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。也就是说,在Maven依赖中,不可能出现groupId和artifactId相同但是version不同的两个依赖。
- 归类依赖
之所以使用归类依赖,是为了保持版本的一致性,排除出错,例如我们通常在整合Spring Framework的时候,有很多都是相同版本的,对此,Maven也提供了归类依赖的方式简单管理版本,这样做的好处就是,将来在升级版本的时候,只需要改这个归类,不需要改所有的依赖版本了。这个就和Java中的常量是一个意思,在Maven中通过EL表达式替换。
归类依赖的语法:
<properties>
<springframeword.version>4.1.5</springframeword.version>
</properties><!— 使用 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframeword.version}</version>
</dependency> - 优化依赖
优化依赖是指:对项目的依赖进行优化,去除多余的依赖,显式的声明某些必要的依赖
使用mvn dependency:list查看当前项目的已解析依赖,依赖范围
使用mvn dependency:tree查看当前的依赖树,可以清晰的看到依赖的引入路径
使用mvn dependency:anapyze分析依赖,只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖就发现不了。
Maven实战07_依赖的更多相关文章
- 【Maven实战】依赖的范围
在Maven中有三大模块,分别是依赖.仓库.生命周期和插件,我们接下来下来介绍下依赖,为了方便起见我们还是以案例来说: 1.首先建立一个maven项目,这里我建立一个user的项目 2.接下来我们在这 ...
- 【Maven实战】依赖的聚合和版本管理
1.在之前的文章中,我们已经建立了四个Maven项目,但是此时如果我们要对这四个项目进行编译打包时,必须一个一个的进行执行命令,而聚合就是指只要我们在其中一个项目中编写一些代码,则在进行此项目的编译和 ...
- Maven实战(六)依赖
我们项目中用到的jar包可以通过依赖的方式引入,构建项目的时候从Maven仓库下载即可. 1. 依赖配置 依赖可以声明如下: <project> ... <dependenci ...
- (转)Maven实战(六)依赖
我们项目中用到的jar包可以通过依赖的方式引入,构建项目的时候从Maven仓库下载即可. 1. 依赖配置 依赖可以声明如下: <project> ... <dependenci ...
- Maven实战(十一)eclipse下载依赖jar包出问题
问题描述 在pom.xml中配置了依赖,但是提示依赖不成功,或在本地仓库找不到相关依赖 大致错误如下: ArtifactDescriptorException: Failed to read arti ...
- [Maven实战](9)传递性依赖
了解Spring的朋友都知道.创建一个Spring Framework项目都须要依赖什么样的Jar包.假设不使用Maven,那么在项目中就须要手动下载相关的依赖.因为Spring Framework又 ...
- Maven实战读书笔记(三):Maven依赖
3.1 依赖的配置 一个依赖声明可以包含下面元素: <dependencies> <dependency> <groupId></groupId> &l ...
- Maven实战:Maven生命周期
前言 之前有写过一篇文章Maven实战,介绍了Maven的一些基本概念,以及对于一个初学者而言的Maven基础知识,当时在我看来掌握了这些基本是够用的. 随着工作的深入,越来越感觉对于Maven的理解 ...
- 学习笔记——Maven实战(四)基于Maven的持续集成实践
Martin的<持续集成> 相信很多读者和我一样,最早接触到持续集成的概念是来自Martin的著名文章<持续集成>,该文最早发布于2000年9月,之后在2006年进行了一次修订 ...
随机推荐
- 应用程序正常初始化(0xc0150002)失败的终极解决方案
转自VC错误:http://www.vcerror.com/?p=62 最近做一个项目写了一个VC6下的MFC程序,结果传到别人的机子上(WIN7)出现了应用程序正常初始化(0xc0150002)失败 ...
- Berlekamp Massey算法求线性递推式
BM算法求求线性递推式 P5487 线性递推+BM算法 待AC. Poor God Water // 题目来源:ACM-ICPC 2018 焦作赛区网络预赛 题意 God Wate ...
- demjson处理json数据
因为json数据不规范出现了以下问题: json.decoder.JSONDecodeError: Expecting property name enclosed in double quo 网上查 ...
- JS流程控制语句 继续循环continue continue的作用是仅仅跳过本次循环,而整个循环体继续执行。
继续循环continue continue的作用是仅仅跳过本次循环,而整个循环体继续执行. 语句结构: for(初始条件;判断条件;循环后条件值更新) { if(特殊情况) { continue; } ...
- [NOIP2019模拟赛][AT2381] Nuske vs Phantom Thnook
题目链接 评测姬好快啊(港记号?)暴力40pts变成60pts 因为题目说了保证蓝色点两两之间只有一条路径,所以肯定组成了一棵树,而对于每次询问的x1,y1,x2,y2的子矩阵中就存在着一个森林 不难 ...
- ubuntu解压/压缩rar文件
一般通过默认安装的ubuntu是不能解压rar文件的,只有在安装了rar解压工具之后,才可以解压.其实在ubuntu下安装rar解压工具是非常简单的,只需要两个步骤就可以迅速搞定.ubuntu 下ra ...
- [JZOJ4769]【GDOI2017模拟9.9】graph
题目 描述 题目大意很明确了,所以不说-- 思考历程 一看见这题,咦,这就是传说中的动态图吗? 普通的动态图是维护连通性,这题是维护它是否是二分图,换言之就是维护它是否有奇环. 好像很复杂的样子. 想 ...
- 【JZOJ3238】【BZOJ3482】超空间旅行
description 在遥远的未来,行星之间的食品运输将依靠单向的贸易路线.每条路径直接连接两个行星,且其运输时间是已知的. 贸易商协会打算利用一项最近发现的新技术--超空间旅行,以增加一些新的航线 ...
- MySQL系列(十二)--如何设计一个关系型数据库(基本思路)
设计一个关系型数据库,也就是设计RDBMS(Relational Database Management System),这个问题考验的是对RDBMS各个模块的划分, 以及对数据库结构的了解.只要讲述 ...
- mybatis结果封装到hashmap中没有null的数据
1.在ssm框架中 在mybatis-config.xml配置文件中加下面这句代码即可解决 <setting name="callSettersOnNulls" value= ...