(二)maven详解之坐标与依赖

看着简单而又复杂的pom.xml文件,看似熟悉,当本身编写的时候以为简单,可是看人家项目的时候又以为复杂的很,如今咱们一块儿来分析这个pom文件。html


Maven的坐标为各类构件引入了秩序,任何一个构件都必须明确的定义本身的坐标,maven的坐标包括以下的元素:spring

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

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

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

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

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

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


元素详解:spa

根元素project下的dependencies元素详解:.net

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属性,而后引用。

示例:

[html] view plaincopy

  1. <properties>  

  2.     <springframework.version>2.5.6</springframework.version>  

  3. </properties>  

这个时候咱们就能够在声明依赖的时候使用${springframework.version}来替换具体的版本号

[html] view plaincopy

  1. <dependency>  

  2.     <groupId>org.springframework</groupId>  

  3.     <artifactId>spring-context-support</artifactId>  

  4.     <version>${springframework.version}</version>  

  5. </dependency>  



如何正确的优化依赖

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

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

mvn dependency : list

通过Maven解析以后,就会构成一个依赖树

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

mvn dependency : tree

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

mvn dependency : analyze

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

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

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

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


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

相关文章
相关标签/搜索