Maven 定义了这样一组规则:世界上任何一个构件都可以使用 Maven 坐标唯一标识, Maven 坐标的元素包括 groupId、 artifactId 、 version 、 packaging 、 classifier 。
Maven 内置了一个中央仓库的地址( http://repo1.maven.org/maven2 ),该中央仓库包含了世界上大部分流行的开源项目构件, Maven 会在需要的时候去那里下载。
groupId 定义了当前 Maven 项目隶属的实际项目。一个实际项目往往会被划分成很多模块,一个模块可能就是一个Maven 项目。 goupId 不应该对应项目隶属的组织或公司,因为一个组织或公司可能会有多个实际项目。 groupId 的表示方式与 Java 包名类似。
artifactId 定义实际项目中的一个 Maven 项目(模块)。推荐使用实际项目名作为 artifactId 的前缀,如: nexus-indexer 。因为默认情况下 Maven 生成的构件会以 artifactId 开头,用实际项目名作为前缀就能方便从一个 lib 文件夹中找到某个实际项目的一组构件。
version 定义了当时 Maven 项目所处的版本。
packaging 定义了 Maven 项目的打包方式,通常与所生成构件的文件扩展名对应。不同的打包方式会影响构建的生命周期。比如 jar 打包与 war 打包会使用不同的命令。不定义 packaging 的时候,默认为 jar 。( package 为 "maven-plugin" 的构件扩展名是 jar )
classifier 定义构建输出的一些附属构件。附属构件与主构件对应。如 Java 项目的 javadoc 和 sources 。 TestNG 项目默认是基于 Java 1.4 平台的,而它又提供了一个 jdk5 的附属构件。我们不能直接定义项目的 classifier ,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成的。
groupId , artifactId , version 是必须定义的, packaging 是可选的, classifier 是不能直接定义的。
项目构件的文件名与坐标相对应,一般为: artifactId-version[-classifier].packaging。
根元素 project 下的 dependencies 可以包含一个或者多个项目依赖。一个依赖声明可以包含如下的一些元素:
<dependency> <groupId>…</groupId> <artifactId>…</artifactId> <version>…</version> <type>…</type> <scope>…</scope> <optional>…</optional> <exclusions> <exclusion>…</exclusion> … </exclusions> </dependency>
groupId 、 artifactId 和 version 是依赖的基本坐标。
type 为依赖的类型,对应于项目坐标定义的 packaging 。
scope是依赖的范围。
optional 标记依赖是否是可选的。
exclusions 用来排除传递性依赖。
依赖范围包含:
compile :编译依赖范围。对于编译、测试、运行三种 classpath 都有效。是默认的依赖范围。
test :测试依赖范围。只对测试 classpath 有效。如 Junit
provided :已提供依赖范围。只对编译、测试 classpath 有效。如 servlet-api ,运行时容器已提供。
runtime :运行依赖范围。只对测试和运行 classpath 有效。如 JDBC 驱动实现,编译主代码时只需要 JDK 提供的JDBC 接口。
system :系统依赖范围。与 Provided 依赖范围一致。但依赖是通过 systemPath 元素显式指定文件的路径,与本机系统绑定,一般无法移植。
import :导入依赖范围。不会对 3 种 classpath 产生实际影响。
Maven 会解析各个直接依赖的 POM, 将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。假设 A 依赖 B , B 依赖 C ,我们称 A 对 B 是第一直接依赖, B 对 C 是第二直接依赖, A 对 C 是传递依赖。
依赖调解第一原则:路径最近者优先。
如:依赖关系为:A->B->C->X(1.0) 、A->D->X(2.0)
结果是X(2.0)会被解析使用。
依赖调解第二原则:最先声明者优先。
如:A->B->X(1.0)、A->D->X(2.0) 且 B 比 D 先声明
结果是 X(1.0)会被解析使用。
如果项目 B 实现了两个特性,特性一依赖于项目 X ,特性二依赖于项目 Y ,而 X 与 Y 是互斥的。(比如 B 是一个持久层隔离包,支持多种数据库, X 为 MySQL 驱动, Y 为 PostgreSQL 驱动。)那么 X 与 Y 是 B 的可选依赖。如果 A依赖 B ,则 A 不再传递依赖 X 与 Y ,而需要显式地声明 X 或 Y 为其直接依赖。
在理想情况下,是不应该使用可选依赖的。因为在面向对象设计中,有个单一职责性原则,意指一个类应该只有一项职责,而不是糅合太多的功能。这个原则在规划maven项目的使用同样适用。在上段文字的说明中,更好的做法是为Mysql和PostgreSQL 分别创建一个maven项目。
有时候希望排除某些传递依赖(比如传递依赖因版权限制不可用或者传递依赖是个 SNAPSHOT 版本)而显示地声明他们的替代者。可以在 <exclusion> 中声明相排除的传递依赖,而多增加一个 <dependency> 声明它的替代者。<exclusion> 只用声明 groupId 和 artifactId 就行。
对于来自同一个实际项目的多个项目依赖,他们版本应该是一致的,这样,可以先用 <properties> 定义一个版本号变量。如:
<properties><spring.version>2.5.6</spring.version></properties> 。
然后在版本信息的位置使用 ${spring.version} 来引用它。
可以执行 mvn dependency:list 或 mvn dependency:tree 查看已解析的依赖( resolved dependency )。
可以执行 mvn dependency:analyze 来分析项目中的直接用到但未显示声明的依赖( Used undeclared dependencies )和声明了但未用到的依赖( Unused declared dependencies )。 Used undeclared dependencies是项目直接用到了,但通过传递依赖引入的项目,这样当项目的直接依赖更新时,传递依赖也会随之更新,从而会导致项目代码与传递依赖不兼容。 Unused declared dependencies 并不是完全没用的依赖,只是在编译主代码和测试代码时没用,但可能会在执行测试和运行时用到。