Maven 依赖机制

一来管理是 Maven 的核心功能。Maven 在定义,创建和维护具有明确定义的类路径和库版本的可复制构建方面大有帮助。

传递依赖关系

Maven 通过自动包含传递性依赖关系,避免了发现和指定你自己的依赖关系所需库的需要(官方文档里的说法)。

也就是说,Maven会根据你引入 pom 的依赖来自动管理各依赖之间的关系,而它们有可能是继承自父项目或者它本身。因此可能会有多个不同版本的同一依赖,然而只有在发生循环依赖的时候才会产生问题。

Maven 自身包含一些功能来限制引入哪些依赖项:

  • Dependency mediation(依赖调制):(官方文档的原词,翻译成依赖调制感觉怪怪的)其实就是在有多个相同依赖的时候选择哪个的规则。Maven 使用 “最近原则”,即在当前项目的依赖树里,选择路径最近的那个依赖。

举个例子来说:

A
  ├── B
  │   └── C
  │       └── Denpendency 2.0
  └── E
      └── Denpendency 1.0

项目A 、B、C、E 的依赖关系如上,项目 A 到 Denpendency 的路径分别为A -> B -> C -> Denpendency 2.0A -> E -> Denpendency 1.0,因此会使用 Denpendency 1.0 作为 项目A 的依赖。当然如果你在项目A 的 pom 的直接声明 Denpendency 2.0,那么就会使用 Denpendency 2.0 作为依赖。

原则上同一项目内声明多个同一依赖的话会按顺序选择第一个,但是实际开发是禁止这样声明的。

  • Denpendency management(依赖管理):当发生依赖传递或者依赖未指定版本的时候,依赖管理允许你来直接指定所使用依赖的版本。在上一部分的示例中,即使项目A没有直接使用依赖 Denpendency,也被添加到了项目A。同样地,A可以在其 dependencyManagement 部分中将 Denpendency 作为依赖引入,并控制何时或是否使用某一版本的 Denpendency。
  • Dependency scope(依赖范围):依赖范围就是依赖在 Maven 构建的哪一阶段起生效,包含了6个范围,后续会细说。
  • Excluded Dependencies(排除依赖):排除依赖就是在当前项目内排除掉依赖的其他项目或里面的某一个依赖。例如,项目 X 依赖于项目 Y,而项目 Y 依赖于项目 Z,则项目 X 的所有者可以使用 “exclusion” 元素将项目 Z 显式排除。
  • Optional Dependencies(可选依赖):我没用过,看文档的意思,好像是默认情况下不包含依赖的样子。例如,项目 Y 依赖于项目 Z,则项目 Y 的所有者可以使用“optional”元素将项目 Z 标记为可选依赖项。当项目 X 取决于项目 Y 时,X 将仅取决于 Y,而不取决于 Y 的可选依赖项 Z。然后,项目 X 的所有者可以选择显式添加对Z的依赖项。

官方推荐的是项目里尽量不要依赖过于庞大的项目,而是显示的申明引用到的依赖。

Maven 提供了很多命令可以查看依赖关系,如mvn dependency:tree 查看依赖树,mvn dependency:analyze 分析依赖引用情况。

依赖范围

依赖范围用于限制依赖的可传递性,并确定依赖何时被引入类路径。

  • compile:默认范围,如果未声明则使用此范围。我的理解是依赖会在编译期生效,即引用的依赖会在编译期被编译进项目的 class 文件里,并且会被传播到相关依赖项目里。例如 Lombok 必须在编译期被编译,如果修改 scope 为 其他类型则项目会编译报错
  • provided:在 JDK 或某容器运行期间已经提供的依赖,只在 compile 和 test 生效,运行期不会生效的一种范围类型。举个栗子,你在家玩 LOL 有一套自己的设备,但是去网咖五连坐就不需要带设备了,因为网咖已经提供了一套设备。此时依赖是不可传递的
  • runtime:这个就很简单了,依赖不是编译所需的,但是运行所必需的。runtime 在 runtime 和 test 都生效,在 compile 不生效
  • test:test 只在测试的编译期和执行期生效,且是不可传递的,而且 test 一般被用在单元测试目录下(src/test/java),不会在源码里使用。例如 JUnit 其依赖范围可以使用 test。

依赖传递

我们的工程,所使用的大多数情况下,不会只有一层依赖关系,例如 A 依赖 B,我们用 A -> B 表示,那么,a -> B,B -> C,则 A 对于 B 是第一依赖,B 对于 C 是第二依赖,而 A 对于 C 是传递性依赖。

传递性依赖的scope传递规则,与第一依赖和第二依赖有关,下表第一列表示第一依赖,第一行表示第二依赖

compile

test

provided

runtime

compile

compile

runtime

test

test

test

provide

provided

provided

provided

runtime

runtime

runtime

上表可以得到几点信息:

  • 第二依赖为 compile 不改变第一依赖
  • 第二依赖 test 不传递以来
  • 第二依赖 provided只传递 provided
  • 第二依赖 runtime 对 compile 第一依赖的依赖传递是 runtime

参考

Apach Maven Project 官方文档