maven具体解释之坐标与依赖

看着简单而又复杂的pom.xml文件,看似熟悉。当自己编写的时候认为简单。可是看人家项目的时候又认为复杂的非常,如今我们一起来分析这个pom文件。


Maven的坐标为各种构件引入了秩序,不论什么一个构件都必须明白的定义自己的坐标,maven的坐标包含例如以下的元素:

groupId: 定义当前Maven项目隶属的实际项目

artifactId: 该元素定义实际项目中的一个Maven项目或模块

version: 该元素定义Maven项目当前所处的版本号

packaging: 该元素定义Maven项目的打包方式

classifier: 该元素用来帮助定义构建输出的一些附属构件

注:groupId、artifactId、version、packaging是必须定义的,classifier是不能被直接定义的,由于附属构件不是项目直接默认生成的。而是由附加的插件帮助生成的。


元素具体解释:

根元素project下的dependencies元素具体解释:

dependencies能够包括一个或者多个dependency元素,以声明一个或多个项目依赖, 其包括的元素:

groupIdartifactIdversion:依赖的基本坐标。对于不论什么一个依赖来说,主要的坐标是最重要的。Maven是依据坐标来找到须要的依赖

type: 依赖的类型

scope: 依赖的范围

optional: 标记依赖是否可选(參见可选性依赖)

exclusions: 用来排除传递性依赖(參见依赖的传递性)


依赖范围具体解释:

Maven在编译项目主代码的时候须要使用一套classpath

Maven在编译和运行測试的时候会使用另外一套classpath

Maven在实际执行项目的时候又会使用一套classpath

依赖范围就是用来控制依赖与这三种classpath(编译classpath、測试classpath、执行classpath)的关系

Maven的6种依赖范围:

compile: 编译依赖范围(默认)。对于编译、測试、执行三种classpath都有效

test: 測试依赖范围, 仅仅对測试classpath有效。典型范例:Junit

provided: 已提供依赖范围 对于编译和測试classpath有效,但在执行时无效。典型范例:servlet-api

runtime: 执行时依赖范围 对于測试和执行classpath有效,但在对编译主代码时无效。典型范例:JDBC

system: 系统依赖范围

import: (maven2.0.9及以上): 导入依赖范围,它不会对三种实际的classpath产生影响

依赖范围(Scope) 对于编译classpath有效 对于測试classpath有效 对于执行时classpath有效 样例
compile Y Y Y spring-core
test   Y   junit
provided Y Y   servlet-api
runtime   Y Y JDBC驱动实现
system Y Y   本地的,Maven仓库之外的类库文件

了解了依赖的基本元素和依赖范围之后。我们会发如今我们项目中常常会出现一些默认的配置问题。导致编译和执行失败的情况,如今让我们来学习怎样解决这些问题,首先要了解一下依赖的传递性


传递性依赖和依赖范围

简单的说,一般项目中出现故障多数是由于反复的引用或者引用了较低版本号的依赖,或者是他们的依赖范围发生了变化。

举个样例来理解传递性依赖:

我们创建了一个Maven Project-----learnDependency,然后我们引入了spring-core这个依赖,然后我们打开spring-core的pom.xml发现,spring-core也有自己的依赖:commons-logging,并且该依赖没有声明依赖范围,那么默认的就是compile,所以这时我们就能够说:commons-logging也是learnDependency的一个依赖,这时我们就将这样的依赖称之为传递性依赖,commons-logging是learnDependency的一个传递性依赖。

有了传递性依赖,我们就能够在使用的时候不去考虑我们引入的依赖究竟是否须要其他依赖,和是否引入多余的依赖,Maven 会解析各个直接依赖的pom。将必要的间接依赖引入到项目中。


细说传递性依赖

如果:A依赖于B,B依赖于C,那么我们就说A对于B是第一直接依赖,B对于C是第二直接依赖。A对于C是传递性依赖。

由于依赖是有依赖范围的。那么对于这样的传递性依赖Maven又是怎样界定其依赖范围的呢?

当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。

当第二直接依赖的范围是test的时候,依赖不会得以传递

当第二直接依赖的范围是provided的时候,仅仅传递第一依赖范围也为provided的依赖,且传递性依赖的范围相同是provided;

当第二直接依赖的范围是runtime的时候。传递性依赖的范围与第一直接依赖的范围一致,但compile除外。此时传递性依赖范围为runtime

  compile test provided runtime
compile compile     runtime
test test     test
provided provided   provided provided
runtime runtime     runtime

左側第一列表示第一直接依赖范围。最上面一行表示第二直接依赖


在我们了解了Maven强大的依赖机制之后,我们開始解决这个问题:

常见问题一:依赖的反复引入

之前说过Maven能够有效的解决依赖的反复引入问题,可是为什么我们在项目还会出现这类问题呢?先让我们来看一下Maven是怎样处理反复引入问题的:


情景一:我们在项目中分别引入了2个依赖A和B,A又依赖的C,C又依赖了D,B也依赖了D,可是这个时候C依赖的D和B依赖的D的版本号是不同的:

项目----A---C----D

项目----B---D

也就是说。当前项目引入了2次D依赖。那么这时,Maven将採用第一原则:路径近期原则


情景二:我们在项目中分别引入了2个依赖A和B,而A和B又都引入了C,可是,此时A依赖的C和B依赖的C版本号是不一致的。那么这个时候Maven怎样处理呢?

这时,第一原则已经不起作用了,

在Maven2.0.8及之前的版本号中  和 Maven2.0.9之后的版本号Maven对于这样的情况的处理方式是不一致的

确切的说:

在Maven2.0.8及之前的版本号中Maven到底会解析哪个版本号的依赖。这是不确定的

Maven2.0.9之后的版本号中。制定了第二原则:第一声明者优先

就是说,它取决于在POM中依赖声明的顺序


这个问题就说明了,为什么我们经常遇到的能够正常执行的项目。然后我们添加了一个看似无关的依赖。然后项目就出现了错误。就是这个传递性依赖搞的鬼。


还要补充说明的一种情况是可选依赖

为什么会有可选依赖呢?是由于某一个项目实现了多个特性。可是我们在面向对象的设计中。有一个原则叫:单一职责性原则。就是强调在一个类仅仅有一项职责,而不是糅合了太多的功能,所以一般这样的可选依赖非常少会出现。


常见问题二:默认引入的依赖----第二直接依赖的版本号过低或者依赖了不稳定的快照

这个问题我们在开发中也常常遇到,在某个第二直接依赖中引入了1.0版本号,可是我们如今想使用2.0版本号,这时我们要怎样解决?

引入一个名词:排除依赖。也能够叫替换依赖

想实现依赖排除,然后替换成自己想要的依赖,这时我们要用到的一个配置是<exclusions>和<exclusion>。我们能够使用这一元素声明排除依赖,然后显示的声明我们想要的依赖。在<exclusions>中能够声明一个或多个<exclusion>来排除一个或多个传递性依赖。

注:声明<exclusion>的时候仅仅须要声明groupId和artifactId就能唯一定位依赖图中的某个依赖。

A ------->  B ------×----C(version1.0)

|

C(version2.0)


常见问题三:解决反复的配置

我们在开发中也常常遇到这种情况,比方在使用spring framework的时候。他们都是来自于同一个项目的不同模块,因此这些依赖的版本号都是同样的,并且在将来升级的时候,这些版本号也会一起被升级,这时Maven又提供了一种解决方式------使用properties元素定义Maven属性。然后引用。

演示样例:

<properties>
	<springframework.version>2.5.6</springframework.version>
</properties>
这个时候我们就能够在声明依赖的时候使用${springframework.version}来替换详细的版本

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>${springframework.version}</version>
</dependency>


怎样正确的优化依赖

首先我们必需要对maven的依赖处理方式了然于胸,然后我们就能够去除多余的依赖,显示的声明必要的依赖,保证每一个构件都仅仅有唯一的版本号在依赖中存在

使用命令来查看当前项目的已解析依赖:

mvn dependency : list

经过Maven解析之后,就会构成一个依赖树

也能够使用命令查看当前项目的依赖树:

mvn dependency : tree

使用命令分析当前当前项目的依赖:

mvn dependency : analyze

该命令运行结果的两个重要部分:

Used undeclared dependencies: 表示项目中使用到的,可是没有显示声明的依赖

Unused declared dependencies: 表示项目中未使用的。但显示声明的依赖

注:dependency : analyze仅仅会分析编译主代码和測试代码须要用到的依赖,一些执行測试和执行时须要的依赖它无法发现。


对于项目中的最佳实践,须要自己多多的尝试或者看别人的一些分享,这样对于开发效率会有非常大的帮助,当然在项目开发的过程中不断的优化和调整这样的方法也未尝不可。



古人说:“覆水难收”。讲话就像泼水,泼出去的水无法再收回,讲过的话也一样收不回来。所以一句话要出口曾经。不能不慎思。讲话是一门艺术,即使讲好话。也要顾虑不能「洗脸碍了鼻子」,你讲这个人好。得罪了那个人,话就讲得不够高明了。讲不好的话。让两方听了都不高兴,当然就更不能讲了



原文地址:https://www.cnblogs.com/wgwyanfs/p/7294483.html