3 Maven坐标和依赖
一、何为Maven坐标
Maven定义了这样一组规则:世界上任务一个构件都可以使用Maven坐标唯一标示,Maven坐标元素包括groupId、artifactId、version、packaging、classifier。
Maven内置了一二中央仓库,该仓库包含世界上大部分的开源项目构件。所以需要使用坐标标示每一个jar文件。
二、坐标详解
①groupId:定义当前Maven项目隶属的实际项目。Maven项目和实际项目不一定一对一关系,比如SpringFramework这一实际项目,其对应的Maven项目会有很多spring-core,spring-context等,这是由于Maven中模块的概念,一个实际项目往往会被划分成很多模块。groupId不应该只对应公司,还需要定义到项目。格式应该与Java包表示方式类似,通常与域名反向一一对应。
②artifactId:该元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀,模块作为后缀。
③version:定义Maven项目当前所处的版本。
④packaging:定义Maven项目的打包方式,首先,打包方式通常与所生成构件的文件扩展名对应。其次,打包方式会影响构建的生命周期,比如jar打包和war打包会使用不同的命令。默认使用jar。
⑤classifier:用来帮助定义构建输出的一些附属构建。附属构件与主构建对应,例如主构建是k12-web-1.00.jar,该项目可能还通过一些插件生成例如k12-web-1.00-javadoc.jar、k12-web-1.00-sources.jar这样两个附属构件的classifier。这样附属构件也有自己的唯一坐标。
总结:以上五个元素,groupId、artifactId、version是必须定义的,packaging是可选的,默认为jar,而classifier是不能直接定义的。
三、依赖的配置
当我们的工程需要对其他构件进行依赖的时候,需要配置dependence,如上例子的依赖: groupId、artifactId、version、packaging上一小节已经介绍了,之后章节介绍其他的。
四、依赖范围
<scope></scope>标签
依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven存在以下几种依赖范围。
①compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,在编译、测试和运行的时候都需要使用该依赖。
②test:测试依赖范围。只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此依赖。典型例子是Junit。
③provided:已提供依赖范围。对于编译和测试classpath有效,但在运行时无效。典型例子是servlet-api,编译和测试项目的时候需要该依赖,但是运行项目的时候,由于容器已经提供,就不需要Maven重复的引入一遍。(servlet的创建是通过容器例如tomcat启动的时候加载文件到内存中创建的,所以不需要重复依赖)。
④runtime:运行时依赖范围。对于测试和运行classpath有效,但是在编译主代码时无效。典型例子jdbc驱动实现,项目主代码的编译只需要jdk提供的jdbc接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体jdbc驱动。
⑤system:系统依赖范围。和privated依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示的指定依赖文件的路径。由于此类的依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。可以引用环境变量,例如:
⑥import:导入依赖范围。此依赖范围不会对三种classpath产生实际的影响。之后会详细介绍。
总结:如下表显示依赖范围与classpath的关系
五、依赖传递性
5.1 何为传递性依赖
当我们依赖的jar包,而此jar包还依赖别的jar包的时候,我们需要同时将这些依赖的jar包都依赖上,而去找却很麻烦。
account-email依赖spring-core包,而spring依赖Commons-logging。
有了传递性依赖机制,Maven会解析各个直接依赖的pom,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。
5.2 传递性依赖和依赖范围
假设A依赖B,B依赖C,那么A对于B是第一直接依赖,B对C是第二直接依赖,A对C是传递性依赖;第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。如下表,左边一列表示第一直接依赖范围,上面一行表示第二直接依赖范围。中间的交叉单元表示传递性依赖范围。
六、依赖调解
上一节讲述了传递依赖,如果两个传递依赖都使用了相同的jar包,则选择哪个作为被解析的呢?第一原则:路径最近优先,例如A->B->C->D(1.0)和F->D(2.0),两条路径都依赖D,则按照最近的原则,则解析D(2.0)版本。
如果路径相同,则第一依赖关系在pom文件中顺序执行。哪个依赖声明靠前,解析哪个。
七、可选依赖
如果存在依赖A->B、B->X(可选)、B->Y(可选)。如果这三个依赖都是compile的,X、Y就是A的传递性依赖,但是X、Y是可选依赖,X、Y将不会对A有任何影响。
使用可选依赖参数解决:<optional>true</optional>
八、最佳实践
主要介绍一些使用maven依赖常用的技巧,方便用来避免和处理很多常见的问题。
8.1 排除依赖
当我们依赖某个类库X的时候,而这个类库还依赖其他类库Y,由于传递依赖,我们也将解析Y,而Y有可能是不稳定版本,也有可能中央仓库不存在,则我们需要排除对Y的依赖而自己去声明一个对Y其他替代版本的依赖。
例子:
项目A依赖项目B,但是由于一些原因,不想引入传递依赖C,而是自己显示的生猛对于项目C1.1.0版本的依赖,例子中使用exclusions元素声明排除依赖,exclusions可以包含一个或者多个exclusion子元素。注意:声明exclusion时只需要groupId和artifactId,因为这两个元素就能唯一定位依赖图中的某个依赖,Maven解析后的依赖中,不可能出现groupId和artifactId相同但是版本不同的两个依赖。
8.2 归类依赖
例如我们依赖spring的jar包,好几个但是版本是一致的,所以如果升级的时候一起升级,所以版本统一管理更好。
例子如下:
<properties>
<org.springframework-version>4.0.0.RELEASE</org.springframework-version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework-version}</version>
</dependency>
这样升级的时候一块升级,方便!!
8.3 优化依赖
Maven会自动解析所有项目的直接依赖和传递性依赖,并且根据规则正确判断每个依赖的范围,对于一些依赖冲突,也能进行调节,以确保任何一个构件只有唯一的版本在依赖中存在,这些工作之后,最后得到那些依赖被称为已解析依赖。
可以使用命令查看当前项目的已解析依赖:mvn dependency :list。或者使用mvn dependency :tree查看依赖树。
当发现项目中没有使用到的构件在依赖树种,我们可以将其删除掉。不过需要谨慎!!!!!