Java的包管理

包是 Java 语言的基本结构,它包含程序运行的基本单元–类,它就是把若干类放在一起打的压缩包。Java 使用全限定类名来区分不同的类。JVM 在执行一个类的字节码的之前,会在 classpath 中根据类的全限定类名唯一确定一个类。

在这个开源的世界,几乎任何Java应用都会依赖一些第三方开源类库,随着依赖的增多,版本不一致,版本冲突,依赖臃肿的问题都会接踵而来。手工的解决这些问题即麻烦也容易出错。所以处理好包之间依赖关系,告诉 JVM 如何找到所需的类库,以及成功地解决其中的冲突问题就是包管理的本质。

Maven-包管理工具

幸运的是 Maven 提供了一个优秀的解决方案,它通过一个坐标系统准确地定位仓库中的每一个包,借助坐标系统来有序地管理依赖,轻松地解决依赖问题。

Maven 默认还为 Java 开发者提供了一个免费的中央仓库,在其中可以找到几乎所有流行的开源库。

Maven 对于项目目录结构、测试用例等内容都有既有的规定,使得只要遵循了这些成熟的规则,用户在项目间切换的时候就免去了额外的学习成本,可以说是约定优于配置 (Convention Over Configuration)

Maven 坐标

Maven 坐标唯一标识一个包,Maven 坐标的元素包括 groupId、artifactId、version,只要我们在 pom.xml 中提供正确的坐标元素,Maven 就能找到对应的包。

Maven 依赖配置

下面罗列了一些简单的依赖配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<project>
<modelVersion>4.0.0</modelVersion>

<properties>
...
</properties>

<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
...
</dependencies>
</project>

Maven 依赖范围

Maven 依赖范围就是用来控制依赖与这三种 classpath (编译、测试、运行)的关系,Maven 有一下几种依赖范围:

  • compile:编译依赖范围。如果没有指定,就是默认使用该 scope,使用此 scope 的 Maven 依赖,对于编译、测试、运行三种 classpath 都有效。
  • test:测试依赖范围。使用此 scope 的 Maven 依赖,只对于测试 classpath 有效。
  • provided:已提供依赖范围。使用此 scope 的 Maven 依赖,对于编译 classpath 有效,但在运行时无效。

Maven 传递性依赖

Maven 引入的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部分情况下我们只需关系项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖。它的原则是:绝对不允许最终的 classpath 出现同名不同版本的 jar 包

但有时候,当传递性依赖造成的问题的时候,我们就需要清楚地知道该传递性依赖是从哪条依赖路径引入的。

一般当你看到如下异常的时候,就要想到可能是传递性依赖造成的问题。

  • AbstractMethodError
  • NoClassDefFoundError
  • ClassNotFoundException
  • LinkageError

例如,项目有这样的依赖关系:A -> B -> C -> X (1.0.0) 、A -> D -> X (2.0.0),X 是 A 的传递性依赖,但是两条依赖路径上有两个版本的 X,那么哪个 X 会被 Maven 解析使用呢?两个版本都出现显然是不行的,因为那会造成依赖重复,因此必须选择一个。Maven 解决依赖冲突的原则是:路径最近者优先。所以 X (2.0.0) 会被选择使用。

Maven 仓库

对于 Maven 来说,仓库只分为两类:本地仓库和远程仓库。当 Maven 根据坐标寻找包的时候,它首先会查看本地仓库,如果本地仓库存在此包,则直接使用;如果本地仓库不存在此包,或需要更新此包版本,Maven 就会去远程仓库查找,发现了之后,下载到本地仓库再使用。如果本地仓库和远程仓库都没有 Maven 就会报错。

默认情况下,Maven 的本地仓库在用户目录路径名为 ~/.m2/repository 的仓库下,下载好的第三方包缓存在这里。

Maven 不仅是一个优秀的依赖管理工具,而且也是一个标准化构建工具