Maven坐标
Maven定义了这样一组规则,世界上任何一个构件都可以使用Maven坐标唯一标识,Maven坐标的元素包括groupId、artifactId、version、packaging、classifier(通常不用)。只要我们指定了一组Maven坐标,Maven就能在其维护的*仓库(http://reop1.maven.org/maven2)中找到相应构件(如果版权允许的话)。
坐标含义详解
例如有如下坐标:
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<packaging>jar</packageing>
这是nexus-indexer的坐标定义,它是一个对Maven仓库编纂索引并提供搜索功能的类库。下面详解各标签含义。
- groupId : 定义当前项目隶属的实际项目。org.sonatype 是sonatype公司的反向域名,nexus是该公司的研发的一个项目。
- artifactId :定义实际项目中的一个模块。建议用实际项目名作为该名称的前缀,方便查找。如nexus-indexer表示nexus项目中的indexer模块。
- version:定义当前项目的版本。带有snapshot标记的表示正在开发中。
- packaging:定义Maven项目打包方式。若不指定,则默认使用jar方式。还可以选择war来对webapp项目打包。 此例项目最终会生成nexus-indexer-2.0.0.jar文件。
项目依赖
Maven使用依赖的概念来管理项目所引用的包,通过在pom文件中配置依赖,Maven在使用类包会自动根据pom中的配置与*仓库下载。
配置依赖
<project>
...
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<type>...<type>
<scope>...<scope>
<optional>...<optional>
<exclusions>
<exclusion>...<exclusion>
<exclusions>
<dependency>
<dependencies>
...
<project>
-
<dependencies>
下可以有多个<dependency>
表示项目中的多个依赖。 -
<groupId><artifactId><version>
定义了依赖的基本坐标,Maven通过这组坐标才能找到相应的依赖。 -
<type>
定义依赖的类型,对应于项目坐标下的packaging,默认为jar -
<scope>
定义依赖的范围,默认为compile。Maven项目在编译、测试、运行时会使用三套不同的classpath,而不同的依赖范围会在不同的classpath中起作用。
依赖范围 | 对于编译classpath有用 | 对于测试classpath有用 | 对于运行classpath有用 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | Spring-core |
test | N | Y | N | JUnit |
provided | Y | Y | N | Servlet-api |
runtime | N | Y | Y | JDBC驱动 |
传递性依赖
A------> B ------> C
若A依赖B,B依赖C,则A间接依赖于C,即为传递性依赖。例如:
StrutsProject ------> struts2-core ------> common-logging
则在实际项目StrutsProject中只需引入struts2-core依赖,则由于传递依赖的特性,Maven会自动下载common-loggging依赖。
依赖调解
若项目通过两条途径引入了同一个依赖,如common-logging,但是它们版本不同,即发生了版本冲突问题。Maven提供了两条原则来解决版本冲突的问题。
- 路径最近者优先
- 声明靠前者优先
假设有如下项目依赖,均按声明顺序书写如下:
A ------> C-2.0 ; A ------> B ------> C-1.0
则依赖C发生了版本冲突,C-2.0的路径长度为1,C-1.0的长度为2,则应该采用C-2.0。
若依赖关系改变如下:
A ------> D ------> C-1.4; A------> B ------- > C-1.0
则两个版本的C路径长度都相同,但由于C-1.4先声明,则应采用C-1.4
排除依赖
若有如下依赖关系
A ------> B ------> C-1.0-snapshot
项目A直接引用依赖B,传递性依赖于C,但是项目B所依赖的C还是开发版,并不稳定,若在项目中引入会导致项目的不稳定性,此时需要排除依赖C-1.0-snapshot,并引入C的稳定版本。A的pom.xml文件如下
...
<dependencies>
<dependency>
<groupId>com.maven</groupId>
<artifactId>project-B</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.maven</groupId>
<artifactId>project-c</artifactId>
<exclusion>
<exclusions>
<dependency>
<dependency>
<groupId>com.maven<groupId>
<artifactId>project-c<artifactId>
<version>1.1<version>
<dependency>
</dependencies>
...
注意exclusion
元素中排除的依赖,其坐标可由groupId和artifactId确定,因为此项目中该依赖版本唯一。
归类依赖
通常我们的项目会引入一组相关的依赖,如使用spring框架,则需要引入spring-core-2.5.6,spring-context-2.5.6 spring-beans-2.5.6等等,当spring框架升级时,如果我们手动地更改这些依赖的版本则略显繁琐,此处我们引入依赖归类的方法,将版本定义为pom.xml的一个属性,一组依赖引用统一的版本属性。如下:
<project>
...
<properties>
<spring.version>2.5.6<spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<dependency>
<dependencies>
...
</project>
归类依赖后,维护spring组件的版本只需对spring.version
进行维护了。
优化依赖
- 去除多余的依赖
- 显示声明某些必须的依赖
Maven在解析项目pom文件后,会自动从*仓库下载直接依赖和传递性依赖,并且调节依赖。这些工作后最后得到的依赖称为已解析依赖。可分别用如下两条命令查看项目的已解析依赖。
mvn dependency:resolved
mvn dependency:list
这些依赖最后会形成一颗依赖树,通过依赖树可以很清楚地看见依赖的引入路径。可使用如下命令查看依赖树
mvn dependency:tree
典型的依赖树如下所示:
除了通过这些命令查看项目的依赖关系,还可以使用如下命令来分析项目依赖中的缺陷
mvn dependency:analyze
例如对上图项目使用该命令如图
该项目中有两种项目缺陷:
Used undeclared dependencies
表示该dependency被项目直接使用,(例如import 该包,并直接使用它),但是并没有被项目直接声明(在pom文件中用dependency元素显示声明),而是由相关依赖传递而来。由依赖树图可知commons-io是由velocity构件间接引入,但是在程序中直接使用了。这种缺陷虽然在编译运行时都不会产生错误报告,但是会影响项目的稳定性。当对直接依赖velocity升级时,传递性依赖commons-io的版本也会相应变动,这种变动有可能导致程序出错(例如接口的变动),而这种错误不易察觉。故在项目中谨记:显示声明任何项目中直接使用的依赖Unused declared dependencies
表示项目中显示声明的但在编译阶段未使用的依赖。对这类依赖需谨慎处理,可能部分依赖会在测试和运行阶段使用。
小结
本文主要介绍了Maven项目的两个概念,坐标与依赖。文中简要介绍了坐标的组成和含义,并详细介绍了依赖的配置过程以及实践过程中常用到的处理依赖关系的方法。通过本文,读者能够比较透彻地了解到Maven的依赖关系,掌握Maven坐标的写法和含义。