Maven学习笔记之坐标和依赖

前言:我在写这篇博客的时候也是一名初学者,如果有任何疑问或者发现某处错误,可以留言或者发送邮件到 fanxiaobin.fxb@qq.com ,我会尽快解决并予以回复。

1. Maven坐标

  Maven坐标其实与地理坐标类同,就是几个可以唯一确定某个东西的参数。在Maven坐标中有三个是必须定义的(groupId、artifactId、version),一个是可选的(packaging),还有一个是不能直接定义的(classifier)。

  Maven坐标为各种构件引入了秩序,任何一个构件都必须明确定义自己的坐标。

  项目构件的文件名是与坐标相对应的,一般规则为artifactId-version[-classifier].packaging。

1.1 groupId

  groupId元素定义了当前Maven项目隶属的实际项目。

  在groupId命名的时候不应该对应项目隶属的组织和公司,原因很简单,一个组织下会有很多实际项目。其命名规则与Java包名的表示方式类似,通常是域名反向一一对应。例如:org.sonatype.nexus

1.2 artifactId

  artifactId元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId作为前缀。这样做的好处是方便寻找实际构件。在默认情况下,Maven生成的构件,其文件名会以artifactId作为开头。

1.3 version

  version元素定义Maven项目当前所处的版本。需要注意的是,Maven定义了一套完整的版本规范,以及快照(SNAPSHOT)的概念,此处不详细说明。

1.4 packaging

  packaging元素定义Maven项目的打包方式。打包方式通常与所生成构件的文件扩展名对应,默认值时jar。

1.5 classifier

  classifier元素用来帮助定义构件输出的一些附属构件。例如我们下载jar包时通常遇到的doc或sources文件。

注意:不能直接定义项目的classifier,因为附属构件不是项目直接默认生成的,而是由附加的构件帮助生成的。

2. 依赖

  任何项目都会或多或少的需要第三方的依赖,如果我们手动的去其官网搜索,也是可以的,但是大部分的官方风格迥异,找起来有点繁琐,更麻烦的是后期的更改,如果发生依赖冲突或升级,我们需要手动的去更改,太繁琐了。Maven很好的帮我们解决了这一类问题,Maven会根据POM配置自动下载及维护所需要的以来构建。

  Maven依赖的配置是在根元素下的dipendencies元素中定义一个或多个dependency元素,来声明一个或多个项目的依赖,每个依赖可以包含的元素有:

  • groupId、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的。
  • type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar。
  • scope:依赖的范围。
  • optional:标记依赖是否可选。
  • exclusions:用来排除传递性依赖。

  大部分时候依赖声明只包含基本坐标,然而在一些特殊情况下,其他元素至关重要。

2.1 scope(依赖范围)

  首先我们需要知道,Maven在编译项目主代码的时候需要使用一套classpath,依赖范围就是用来控制依赖与三种classpath(编译,测试,运行)的关系,Maven有以下几种依赖范围:

  • compile:编译依赖范围。该选项是默认值,使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。
  • test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效。
  • provide:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。
  • runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码是无效。
  • system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径。由于此类以来不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构件的不可移植,因此应该谨慎使用。systemPath元素可以引用环境变量。
  • import:导入依赖范围。(Maven2.0.9及以上)。

2.2 传递性依赖

  我们项目需要依赖第三方构件,同样第三方构件也可能依赖其它构件,这是很常见的问题,我们了解我们使用的第三方构件这个依赖,但是我们有必要再去了解第三方构件所需要的依赖吗,很显然,我们没必要了解。我们的项目与第三方构件的依赖就构成了传递性依赖,有了传递性依赖机制,在使用第三方构建的时候,就不用考虑它依赖了什么,也不用担心引入多余的依赖。Maven会揭西格格直接依赖的POM,讲那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。

  依赖范围不仅可以控制依赖与三种classpath的关系,还对依赖传递性依赖产生影响。假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。

规律:

  1. 当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
  2. 当第二直接依赖的范围是test的时候,依赖不会得以传递。
  3. 当第二直接依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为provided。
  4. 当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。

2.3 依赖调节

  Maven引入的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖。但有时候,当传递性依赖造成问题的时候,我们就需要清楚地知道传递性依赖是从那条依赖路径引入的。

Maven依赖调节(Dependency Mediation)的第一原则是:路径最近着优先。

Maven依赖调节(Dependency Mediation)的第二原则是:第一声明者优先。

  举例来说:A->B->C->X(1.0)、A->D->X(2.0),其中X是A的传递性依赖,根据第一原则,X(2.0)会被解析使用。如果路径相同的话就会根据第二原则,谁先声明,就是用谁。

2.4 optional(可选依赖)

  场景:项目A依赖于B,项目B依赖于项目X和Y,X和Y是可选的,换句话说这两个项目不能同时存在。常见的是B是一个持久层隔离工具包,它支持多种数据库,在构件这个工具包的时候,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。

注意:可选依赖声明,只会对当前的项目产生对象,也就是上面的项目B,其他项目依赖于B的时候,这两个依赖不会被传递,因此当项目依赖于项目B的时候,如果其实际使用的是X或Y,那么必须显示的声明这个依赖。

说明:源于可选依赖需要说明的一点是,在理想的情况下,是不应该使用可选依赖的。

2.5 exclusions(排除依赖)

  传递性依赖会给项目隐士地引入很多依赖,这极大地简化了项目依赖的管理,但是有些时候这种特性也会带来问题。举例来说,项目第三方依赖同时依赖着一个SNAPSHOT版本的类库,这样是很危险的,所以我们需要将他指向一个稳定的版本类库。又或者说项目第三方依赖同时依赖着一个未开源的类库,此时,你有一个对应的实现,此时,你就可以排除掉原有的依赖,指向自己制定的类库。

注意:

  1. exclusions是一个相对的根目录,其包含的多个exclusion是用来指定排除依赖。
  2. 声明exclusion的时候只需要groupId和artifactId,而不需要version元素。(不会出现groupId和artifactId相同,但是version不同的两个依赖)。

2.6 归类依赖

  场景:使用同一项目的不同模块,这些依赖的版本都是相同的,如果要升级这些模块,就需要一起升级。

我们使用properties元素来定义一个属性,然后就可以通过美元符号和大括号的方式引用这个属性了。

示例:

<properties>
	<springframework.version>2.5.6</springframework.version>	
</properties>

<dependencies>
	<dependency>
		<groupId>******</groupId>
		<artifactId>****</artifactId>
		<version>${springframework.version}</version>
	</dependency>
</dependencies>

2.7 优化依赖

  Maven会自动解析所有项目中的直接依赖和传递性依赖,并根据规则正确判断每个依赖的范围,对于一些依赖冲突,也能进行调节,以确保任何一个构建只有唯一的版本在依赖中存在。在这些工作之后,最后得到的那些依赖被称为已解析依赖(Resolve Dependency)。

我们可以通过如下命令查看当前醒目的已解析依赖:

mvn dependency:list

运行结果如下:


有上面的基础,这个应该能够看懂:项目名-模块-jar-版本-依赖的范围

我们也可以查看依赖树,命令如下:

mvn dependency:tree

运行结果如下:



  使用dependency:list和dependency:tree可以帮助我们详细了解项目中所有依赖的具体信息,再次基础上,还有dependency:analyze工具可以帮助分析当前项目的依赖。

具体示例可以参考《Maven实战》第五章最后一节,再次只做演示,理解就可以了。

运行结果如下:

  其中Used undeclared dependencies,意思是项目中使用到的,但是没有显示声明的依赖,这里是spring-context。这种依赖意味着潜在的风险,当前项目直接在使用它们,例如有很多相关的Java import声明,而这种依赖是通过直接依赖传递进来的,当升级直接依赖的时候,相关传递性依赖的版本也可能发生变化,这种变化不易察觉,但是有可能导致当前项目出错。例如:由于接口的改变,当前项目中的相关代码无法编译。这种隐藏的、潜在的威胁一旦出错,就往往需要消耗大量的时间来查明真相。因此,显式声明任何项目中直接用到的依赖。

  其中Unused declared dependencies,意思是说项目中未使用的,但显式声明的依赖。需要注意的是:对于这样一类依赖,我们不应该简单地直接删除其声明,而是应该仔细分析。因为dependency:analyze只会分析编译主代码和测试代码需要的依赖,一些执行测试和运行需要的依赖它就发现不了了。当然,有时候确实能通过该信息找到一些没用的依赖,但一定要小心测试。

参考资料:

  • 《Maven实战》

赞赏

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值