Maven是什么
Maven是跨平台的项目管理工具,主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。
什么是构建
除了编写源代码,我们每天有相当一部分时间花在了编译、运行单元测试、生成文档、打包和部署等繁琐而不起眼的工作上,这就是构建
(build
)。
Maven是优秀的构建工具
Maven可以自动化构建过程,从清理、编译、测试到生成报告,再到打包和部署。Maven最大化地消除了构建的重复,抽象了构建生命周期,并且为绝大部分的构建任务提供了已实现的插件,我们不再需要定义过程,甚至不需要再去实现这些过程中的一些任务。
最简单的例子就是测试,我们没必要告诉Maven去测试,更不需要告诉Maven如何运行测试,只需要遵循Maven的约定编写好测试用例当我们运行构建的时候,这些测试便会自动运行。
Maven不仅仅是构建工具
Maven不仅是构建工具,还是一个依赖管理工具盒项目信息管理工具。它提供了中央仓库,能帮助我们自动下载构件。
Maven依赖
根元素project
下的dependencies
可以包含一个或多个dependency
元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
-
groupId
,artifactId
,version
:依赖的基本坐标 -
type
:依赖的类型,大部分情况下不必声明,默认为jar -
scope
:依赖的范围 -
optional
:标记依赖是否可选 -
exclusions
:用来排除传递性依赖
依赖范围
首先要弄清楚,Maven在不同的时候,使用了不同的classpath。Maven在编译项目主代码的时候使用compile classpath;在编译和执行测试的时候使用test classpath;在实际运行Maven项目的时候使用runtime classpath。依赖范围
就是用来控制依赖于这三种classpath(compile classpath, test classpath, runtime classpath)的关系。Maven有以下几种依赖范围:
-
compile
:编译依赖范围,如果没有指定,默认使用该依赖范围 -
test
:测试依赖范围,使用此依赖范围的Maven依赖,只对test classpath有效 -
provided
:已提供依赖范围,使用此依赖范围的Maven依赖,对于compile和test classpath有效,但在运行时无效 -
runtime
:运行时依赖范围,使用此依赖范围的Maven依赖,对于test和runtime classpath有效,但在编译主代码时无效 -
system
:系统依赖范围,其与三种classpath的关系,和provided依赖范围一致
依赖范围与三种classpath的关系如下所示:
依赖范围Scope | 对于compile classpath有效 | 对于test classpath有效 | 对于runtime classpath有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | Y | JUnit | ||
provided | Y | Y | servlet-api | |
runtime | Y | Y | JDBC驱动实现 | |
system | Y | Y | 本地的Maven仓库职位的类库文件 |
传递性依赖
假设A依赖于B,B依赖于C,则A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖
。第一直接依赖和第二直接依赖决定了传递性依赖的范围,如下表所示,最左边一列表示第一直接依赖范围,最上面一行表示第二直接依赖范围,中间的交叉单元格则表示传递性依赖范围。
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | / | / | runtime |
test | test | / | / | test |
provided | provided | / | provided | provided |
runtime | runtime | / | / | runtime |
依赖仲裁
Maven依赖仲裁(Dependency Mediation):
- 第一原则:最短路径优先原则
- 第二原则:第一声明优先原则,在依赖路径长度相等的前提下,在POM中依赖声明最靠前的依赖优先
例如,项目A有这样的依赖关系:
1. A -> B -> C -> X(1.0),A -> D -> X(2.0),X(1.0)路径长度为3,X(2.0)路径长度为2,根据第一原则,X(2.0)会被解析使用;
2. A -> B -> Y(1.0),A -> C -> Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度相同,如果B的依赖声明在C之前,那么Y(1.0)会被解析使用。
依赖相关的命令
-
mvn dependency:list
列出最终确定的依赖 -
mvn dependency:tree
打出依赖树 -
mvn dependency:analyze
依赖分析,给出项目中已使用但未声明的依赖,以及未使用但已声明的依赖
POM优化
依赖管理
- 多模块项目中,定义一个主pom,在主pom中使用
dependencyManagement
定义依赖、版本和依赖排除 - 子模块从主pom中继承依赖,一般情况下,不要在子模块中做依赖排除,也不要指定依赖版本
最佳实践
归类依赖
如果多个相关的依赖的版本都是相同的,可以使用properties
元素定义Maven属性,依赖的版本值用这一属性引用表示。
<properties>
<spring.version>2.5.6</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
参考资料
- Maven实战(Maven in Action)
- Maven权威指南