《Maven官方文档》-Maven依赖机制简介(一)

依赖机制是Maven最为用户熟知的特性之一,同时也是Maven所擅长的领域之一。单个项目的依赖管理并不难,
但是当你面对包含数百个模块的多模块项目和应用时,Maven能帮你保证项目的高度控制力和稳定性。

大纲:

  •  传递性依赖

    • 排除、可选依赖
  • 依赖范围
  • 依赖管理
    • 导入依赖
  • 系统依赖

传递性依赖

传递性依赖是Maven2.0的新特性。假设你的项目依赖于一个库,而这个库又依赖于其他库。你不必自己去找出所有这些依赖,你只需要加上你直接依赖的库,Maven会隐式的把这些库间接依赖的库也加入到你的项目中。这个特性是靠解析从远程仓库中获取的依赖库的项目文件实现的。一般的,这些项目的所有依赖都会加入到项目中,或者从父项目继承,或者通过传递性依赖。
传递性依赖的嵌套深度没有任何限制,只是在出现循环依赖时会报错。
传递性依赖会导致包含库的依赖图增长的非常大。为了解决这个问题,Maven也提供了额外的机制,能让你指定哪些依赖会被包含:

  • 依赖调解 – 当项目中出现多个版本构件依赖的情形,依赖调解决定最终应该使用哪个版本。目前,Maven 2.0只支持“短路径优先”原则,意思是项目会选择依赖关系树中路径最短的版本作为依赖。当然,你也可以在项目POM文件中显式指定使用哪个版本。值得注意的是,在Maven2.0.8及之前的版本中,当两个版本的依赖路径长度一致时,哪个依赖会被使用是不确定的。不过从Maven 2.0.9开始,POM中依赖声明的顺序决定了哪个版本会被使用,也叫作”第一声明原则”。

    • “短路径优先”意味着项目依赖关系树中路径最短的版本会被使用。例如,假设A、B、C之间的依赖关系是A->B->C->D(2.0)和A->E->(D1.0),那么D(1.0)会被使用,因为A通过E到D的路径更短。但如果你想要强制使用D(2.0),那你也可以在A中显式声明对D(2.0)的依赖。
  • 依赖管理 – 在出现传递性依赖或者没有指定版本时,项目作者可以通过依赖管理直接指定模块版本。之前的章节说过,由于传递性依赖,尽管某个依赖没有被A直接指定,但也会被引入。相反的,A也可以将D加入<dependencyManagement>元素中,并在D可能被引用时决定D的版本号。
  • 依赖范围 – 你可以指定只在当前编译范围内包含合适的依赖。 下面会介绍更多相关的细节。
  • 排除依赖 – 如果项目X依赖于项目Y,项目Y又依赖项目Z,项目X的所有者可以使用”exclusion”元素来显式排除项目Z。
  • 可选依赖 – 如果项目Y依赖项目Z,项目Y的所有者可以使用”optional”元素来指定项目Z作为X的可选依赖。那么当项目X依赖项目Y时,X只依赖Y并不依赖Y的可选依赖Z。项目X的所有者也可以根据自己的意愿显式指定X对Z的依赖。(你可以把可选依赖理解为默认排除)。

依赖范围

依赖范围会影响传递性依赖,同时也会影响项目构建任务中使用的classpath。
Maven有以下6种依赖范围:

  • compile
    这是默认范围。如果没有指定,就会使用该依赖范围。编译依赖对项目所有的classpath都可用。此外,编译依赖会传递到依赖的项目。
  • provided
    和compile范围很类似,但provided范围表明你希望由JDK或者某个容器提供运行时依赖。例如,当使用Java EE构建一个web应用时,你会设置对Servlet API和相关的Java EE APIs的依赖范围为provided,因为web容器提供了运行时的依赖。provided依赖只对编译和测试classpath有效,并且不能传递。
  • runtime
    runtime范围表明编译时不需要依赖,而只在运行时依赖。此依赖范围对运行和测试classpath有效,对编译classpath无效。
  • test
    test范围表明使用此依赖范围的依赖,只在编译测试代码和运行测试的时候需要,应用的正常运行不需要此类依赖。
  • system
    系统范围与provided类似,不过你必须显式指定一个本地系统路径的JAR,此类依赖应该一直有效,Maven也不会去仓库中寻找它。
  • import(Maven2.0.9及以上)
    import范围只适用于pom文件中的<dependencyManagement>部分。表明指定的POM必须使用<dependencyManagement>部分的依赖。因为依赖已经被替换,所以使用import范围的依赖并不影响依赖传递。

每类依赖范围(除了import)通过不同方式影响传递性依赖,具体如下表所示。最左侧一列代表了直接依赖范围,最顶层一行代表了传递性依赖的范围,行与列的交叉单元格就表示最终的传递性依赖范围。表中的“-“表示该传递性依赖将会被忽略。

compile provided runtime test
compile compile(*) runtime
provided provided provided
runtime runtime runtime
test test test

(*)注意这里本来应该是compile范围,那样的话compile范围都必须显式指定-然而,有这样一种情况,你依赖的、继承自其它库中的类的库必须在编译时可用。考虑到这个原因,即使在依赖性传递情况下,编译时依赖仍然是compile范围。

 

依赖管理

Maven提供了一个机制来集中管理依赖信息,叫做依赖管理元素”<dependencyManagement>”。假设你有许多项目继承自同一个公有的父项目,那可以把所有依赖信息放在一个公共的POM文件,并且在子POM中简单第引用该构件即可。通过一些例子可以更好的解释这个机制。下面是两个继承自同一个父项目的POM:

项目A

<project>

<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

项目B

<project>

<dependencies>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

这两个POM都依赖于同一个模块,同时每个POM又各自依赖于一个无关的模块。父项目的POM详细信息如下所示:

<project>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>

<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>

</dependency>

<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

这样两个子项目的POM文件就简单多了。

<project>

<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
</dependency>

<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<!– This is not a jar dependency, so we must specify type. –>
<type>bar</type>
</dependency>
</dependencies>
</project>

<project>

<dependencies>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<!– This is not a jar dependency, so we must specify type. –>
<type>war</type>
</dependency>

<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<!– This is not a jar dependency, so we must specify type. –>
<type>bar</type>
</dependency>
</dependencies>
</project>

注意:在这两个POM文件的依赖中,我们必须指定<type/>元素。因为与依赖管理元素匹配的依赖引用最小信息集是{groupId, artifactId, type, classfier}。许多情况下,依赖指向的jar不需要指定classfier。因为默认type是jar,默认classfiler为空,所以我们可以把信息集设置为{groupId, artifactId}。

依赖管理元素第二个非常有用的功能是控制传递性依赖中构件的版本。例子如下
项目A:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>A</artifactId>
<packaging>pom</packaging>
<name>A</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>d</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

项目B:

<project>
<parent>
<artifactId>A</artifactId>
<groupId>maven</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>B</artifactId>
<packaging>pom</packaging>
<name>B</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>d</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>c</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

当在maven中有项目依赖B时,不管它们的pom文件中指定的版本是什么,构件a,b,c和d的版本都是1.0。

  • a和c都被声明为这个项目的依赖,根据依赖调解,a和c的版本都是1.0.同时a和c的依赖范围都被显式指定为runtime。
  • b定义在B的父项目的<dependencyManagement>元素中,因为在依赖性传递中<dependencyManagement>优先于依赖调解,所以b的版本是1.0,b是编译依赖范围。
  • 最后,d是定义在B的<dependencyManagement>元素中。

依赖管理的标签详细描述信息可以从这里获取项目描述符引用

导入依赖

这个章节描述的特性只在Maven2.0.9及之后的版本才有。这意味着更早版本的Maven不会解析包含import元素的pom文件。因此在使用该特性前,你必须慎重考虑。如果你打算使用这个特性,我们建议你使用enforcer插件来强制使用Maven2.0.9及以上版本。

前面的例子描述了怎么通过继承来指定管理的依赖。然而,这对于更大的项目通常会更复杂,因为一个项目只能继承自一个父项目。为了解决这个问题,项目可以导入其他项目的管理依赖,这可以通过声明依赖一个包含值为”import”的<scope>元素的构件来实现。

转载自 并发编程网 - ifeve.com

时间: 2024-09-21 01:47:43

《Maven官方文档》-Maven依赖机制简介(一)的相关文章

《Maven官方文档》POM文件(一)

什么是POM? POM(project object model)包含了工程信息和工程的配置细节,Maven使用POM文件来构建工程.POM文件包含了工程中的大部分默认值.举个例子,target是默认的构建目录,src/main/java是默认的源码目录,src/test/java是默认的测试源码目录,等等. Maven2中的pom.xml就是Maven1中的project.xml.相比于在maven.xml中包含可执行的goal,现在goals和plugins都可以在pom.xml中配置.当执

《Maven官方文档》-Maven依赖机制简介(二)

项目B: <project> <modelVersion>4.0.0</modelVersion> <groupId>maven</groupId> <artifactId>B</artifactId> <packaging>pom</packaging> <name>B</name> <version>1.0</version> <depend

《Maven官方文档》POM文件(二)

工程继承 POM中可配置的元素如下: 依赖 (dependencies) 开发者和贡献者 (developers and contributors) 插件列表,包括报告 (plugin lists, including reports) 相应id的插件执行 (plugin executions with matching ids) 插件配置 (plugin configuration) 资源 (resources) Super POM就是一个工程继承的例子.你也可以通过在POM中指定parent

《Maven官方文档》什么是原型(Archrtype)?

什么是原型? 简单来说,原型(Archetype)是一个Maven工程模板工具包.一个原型被定义为一个提供相同性质内容的原始样式或模型.这个名字适合当我们试图提供一个系统,这个系统提供了一个一致的方式生成Maven项目.原型将帮助作者为用户创建Maven工程模板,并且为用户提供方法去使用参数来调整生成的工程模板. 使用原型提供了一个很好的方式,使开发人员快速的符合你的项目或组织的最佳实践,在Maven项目中,我们提供了一个演示了Maven多种特性的示例工程,以此希望帮助我们的用户尽快的启动并运行

《maven官方文档》5分钟开始Maven

前提 你必须明白如何在电脑上安装软件.如果你不知道如何做,请向你学校.办公室里等的人请教下,或者付费给他人让他们解释给你.Maven邮件组不是寻求这个建议的最好地方. 安装 Maven是个Java工具,所以你必须安装了Java才能继续.首先,下载Maven,然后按照安装指南.完成后,在终端或命令行中键入如下指令: mvn --version 应该会打印已安装Maven的版本.例如: Apache Maven 3.0.5 (r01de14724cdef164cd33c7c8c2fe155faf96

《Maven官方文档》插件的发展

来讲解一下Maven 2.0 插件的发展       maven中有一个核心引擎,它提供了基础项目的处理能力和构建能力的管理,还有很多用于构建任务的插件. 那到底什么是插件呢? Maven 核心框架就是一个插件的集合.换而言之,这些插件就是行为的执行者,插件可以用于:创建jar文件,war文件,编译代码,单元测试,创建项目文档等等.几乎任何的行为,你都可以认为是这个项目在执行对应的Maven插件. 插件是Maven的核心特性并可以给多个项目重复使用.插件执行的这些行为包含在一个项目描述--项目对

《Maven官方文档》创建Archetype

创建archetype是一个非常简单的过程.archetype就是一个非常简单的构件,它包含了你想创建的工程的模型.archetype由这些东西组成: 一个archetype描述符(archetype descriptor)(src/main/resources/META-INF/maven目录下的archetype.xml).这个文件列出了包含在archetype中的所有文件并将这些文件分类,因此archetype生成机制才能正确的处理. 原型文件(prototype files),arche

《Maven官方文档》创建Archetype 原文链接

创建archetype是一个非常简单的过程.archetype就是一个非常简单的构件,它包含了你想创建的工程的模型.archetype由这些东西组成: 一个archetype描述符(archetype descriptor)(src/main/resources/META-INF/maven目录下的archetype.xml).这个文件列出了包含在archetype中的所有文件并将这些文件分类,因此archetype生成机制才能正确的处理. 原型文件(prototype files),arche

《Apache Velocity用户指南》官方文档

Quick Start 本项目是 Apache Velocity官方文档的中文翻译版,Velocity类似与JSP,是一种基于Java的模板引擎.它可以在web页面中引用Java代码中定义的数据和对象,而Velocity的作用就是把Web视图和java代码进行组装在一起.本次翻译主要针对对Velocity感兴趣和工作中使用到Velocity的开发人员提供有价值的中文资料,希望能够对大家的工作和学习有所帮助. 由于我也是第一次接触Velocity,还不是很深入,翻译的时候也查看了一些博客以及其他网