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