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只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖它无法发现。

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

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

maven详解之坐标与依赖,布布扣,bubuko.com

时间: 2024-10-23 05:50:53

maven详解之坐标与依赖的相关文章

Maven学习笔记之——坐标和依赖(上)

Maven学习笔记之--坐标和依赖(上) 1.    Maven坐标概念 Maven通过构件的坐标来在Maven仓库中定位到具体的构件.Maven的坐标元素包括groupId.artifactId.versiion.packaging.classifier.Maven内置了一个中央仓库地址.需要时Maven会根据坐标到其中下载.具体关于中央仓库的介绍在后面. 2.    Maven坐标详解 比如下面一组坐标: <groupId>org.andy.items</groupId> &l

Maven详解之仓库------本地仓库、远程仓库

在Maven中,任何一个依赖.插件或者项目构建的输出,都可以称之为构件. Maven在某个统一的位置存储所有项目的共享的构件,这个统一的位置,我们就称之为仓库.(仓库就是存放依赖和插件的地方) 任何的构件都有唯一的坐标,Maven根据这个坐标定义了构件在仓库中的唯一存储路径, 解读Maven在仓库中的存储路径: 1.基于groupId准备路径,将句点分隔符转成路径分隔符,就是将  "."  转换成 "/" ; example: org.testng --->o

Maven详解之聚合与继承

说到聚合与继承我们都很熟悉,maven同样也具备这样的设计原则,下面我们来看一下Maven的pom如何进行聚合与继承的配置实现. 一.为什么要聚合? 随着技术的飞速发展和各类用户对软件的要求越来越高,软件本身也变得越来越复杂,然后软件设计人员开始采用各种方式进行开发,于是就有了我们的分层架构.分模块开发,来提高代码的清晰和重用.针对于这一特性,maven也给予了相应的配置. 情景分析一: 我们在开发过程中,创建了2个以上的模块,每个模块都是一个独立的maven project,在开始的时候我们可

Maven详解及其环境配置

Maven详解 一.前言     以前做过的项目中,没有真正的使用过Maven,只知道其名声很大,其作用是用来管理jar 包的.最近一段时间在项目过程中使用Maven,用Maven构建的web项目,其项目结构只停留在了解阶段,没有深入的使用与理解,刚好最近看了一篇关于Maven的详解:就开始深入学习一下Maven的具体应用. 二.Maven的作用 在开发中,为了保证编译通过,我们会到处去寻找jar包,当编译通过了,运行的时候,却发现"ClassNotFoundException",我们

(转载)maven详解

转载: http://www.cnblogs.com/hongwz/p/5456578.html Maven详解 一.前言     以前做过的项目中,没有真正的使用过Maven,只知道其名声很大,其作用是用来管理jar 包的.最近一段时间在项目过程中使用Maven,用Maven构建的web项目,其项目结构只停留在了解阶段,没有深入的使用与理解,刚好最近看了一篇关于Maven的详解:就开始深入学习一下Maven的具体应用. 二.Maven的作用 在开发中,为了保证编译通过,我们会到处去寻找jar包

Maven详解(转载)

Maven详解 一.前言     以前做过的项目中,没有真正的使用过Maven,只知道其名声很大,其作用是用来管理jar 包的.最近一段时间在项目过程中使用Maven,用Maven构建的web项目,其项目结构只停留在了解阶段,没有深入的使用与理解,刚好最近看了一篇关 于Maven的详解:就开始深入学习一下Maven的具体应用. 二.Maven的作用 在开发中,为了保证编译通过,我们会到处去寻找jar包,当编译通过了,运行的时候,却发现"ClassNotFoundException",我

Maven详解 之 聚合与继承

说到聚合与继承我们都很熟悉,maven同样也具备这样的设计原则,下面我们来看一下Maven的pom如何进行聚合与继承的配置实现. 一.为什么要聚合? 随着技术的飞速发展和各类用户对软件的要求越来越高,软件本身也变得越来越复杂,然后软件设计人员开始采用各种方式进行开发,于是就有了我们的分层架构.分模块开发,来提高代码的清晰和重用.针对于这一特性,maven也给予了相应的配置. 情景分析一: 我们在开发过程中,创建了2个以上的模块,每个模块都是一个独立的maven project,在开始的时候我们可

maven详解之生命周期与插件(二)

插件配置 定义解释:插件目标 当我们了解了maven插件之后,我们发现如果为每一个功能编写一个独立的插件显然是不可取的,因为这些任务背后有很多可以复用的代码,因此,把这些功能聚集在一个插件里,每一个功能我们就称之为一个插件目标. 举个例子: maven-dependency-plugin有十多个目标,每个目标对应了一个功能 分析项目依赖:dependency:analyze 列出项目依赖树:dependency: tree 列出项目所有已解析的依赖:dependency:list POM中插件全

maven如何下载和安装, Maven详解

[项目管理和构建]--Maven下载.安装和配置(二) blog.csdn.net 2015年05月01日 14:22 在上篇博文[项目管理和构建]--Maven简介(一)中我们了解到maven是一种全新的项目构建方式,让我们的开发更加简单,高效.Maven主要做的是两件事: 统一开发规范与工具 统一管理jar包 这两件事情他都做到了,而且还做了更多的事情.Maven还可以管理项目的整个声明周期,包括编 译,构建,测试,发布,报告等等.目前Apache下绝大多数项目都已经采用Maven进行管理.