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

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

原文地址 译者:Tyrianhtml

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

大纲:web

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

 

传递性依赖

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

  • 依赖调解 – 当项目中出现多个版本构件依赖的情形,依赖调解决定最终应该使用哪一个版本。目前,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种依赖范围:spring

  • 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)经过不一样方式影响传递性依赖,具体以下表所示。最左侧一列表明了直接依赖范围,最顶层一行表明了传递性依赖的范围,行与列的交叉单元格就表示最终的传递性依赖范围。表中的“-“表示该传递性依赖将会被忽略。sql

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

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

 

依赖管理

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

项目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>api

项目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>元素的构件来实现。

项目B:

<project>
<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>maven</groupId>
<artifactId>A</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>

假设A就是上一个例子中定义的POM,那么最终的结果也是一致的。除了在B中定义的d模块,全部A的管理依赖都会导入到B中。

项目X:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>X</artifactId>
<packaging>pom</packaging>
<name>X</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>a</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>test</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

项目Y:

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

项目Z:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>maven</groupId>
<artifactId>Z</artifactId>
<packaging>pom</packaging>
<name>Z</name>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>maven</groupId>
<artifactId>X</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>maven</groupId>
<artifactId>Y</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

在上面的例子中,Z导入了X和Y的管理依赖。不过有个问题,X和Y都包含了依赖a。在这里,会使用1.1版本的a,由于X先被声明,而且a没有在Z的依赖管理中声明。

这个过程是递归进行的。假如X导入了另外的POM,Q,那么当解析Z的时候,全部Q的管理依赖看上去就都像在X中定义的同样。

当定义一个用于构建多项目的包含一些相关构件的依赖“库”时,导入依赖就十分有效。从“库”中引用一个或多个构件到项目中,是一种很常见的作法。然而,
保持项目中使用的依赖版本与库中发布的版本一致会有点麻烦。下面的模式描述了怎么生成一个供其它项目使用的“物料清单”(BOM)。

项目的根元素是BOM pom文件。它定义了库中建立的全部构件版本。其它要使用该库的项目必须将该pom导入到其pom文件中的<dependencyManagement>元素中。

<project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project1Version>1.0.0</project1Version>
<project2Version>1.0.0</project2Version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project1Version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>parent</module>
</modules>
</project>

parent子项目将BOM pom做为它的父项目。这是一个简单的多项目pom。

<project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project1Version>1.0.0</project1Version>
<project2Version>1.0.0</project2Version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project1Version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>parent</module>
</modules>
</project>

接下来是真正的pom文件。

<project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.test</groupId>
<version>1.0.0</version>
<artifactId>parent</artifactId>
</parent>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
<version>${project1Version}</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
</project>

<project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.test</groupId>
<version>1.0.0</version>
<artifactId>parent</artifactId>
</parent>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
<version>${project2Version}</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
</dependencies>
</project>

下面的例子说明了怎么在项目中使用“库”,而没必要指定依赖模块的版本。

<project xmlns=”http://maven.apache.org/POM/4.0.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>use</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>project1</artifactId>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>project2</artifactId>
</dependency>
</dependencies>
</project>

最后,当建立引入依赖的项目时,须要注意如下几点:

  • 不要尝试引入在当前pom中定义的子模块pom。那会致使不能定位pom和编译失败。
  •  毫不要声明导入其余pom做为目标pom的父项目(或者祖父项目等)的pom文件。这会致使循环解析,并触发异常。
  •  当引用有传递性依赖的模块时,须要指定依赖模块的版本。不这样作,这些模块可能没有肯定的版本,从而致使编译失败。(这在任何状况下都应该是一个最佳实践,由于它保证了模块版本的不变性)

系统依赖

系统范围的依赖应该是一直可用,而且Maven不会去仓库中查找。系统范围依赖一般是指JDK或者VM提供的依赖。因此,系统依赖适用于这种状况:之前能够单独获取,但如今是由JDK提供的依赖。典型的例子就是JDBC标准扩展或者Java认证和受权服务(JAAS)。一个简单的例子以下:

<project>

<dependencies>
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
</dependencies>

</project>

若是你的构件依赖于JDK的tools.jar,那么系统路径的值以下所示:

<project>

<dependencies>
<dependency>
<groupId>sun.jdk</groupId>
<artifactId>tools</artifactId>
<version>1.5.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>

</project>

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文连接地址: 《Maven官方文档》-Maven依赖机制简介

Tyrian

Software Engineerat TP-LINK

Favorite添加本文到个人收藏

Related Posts:

  1. 《Maven官方文档》建立Archetype
  2. 《Maven官方文档》POM文件
  3. 《maven官方文档》5分钟开始Maven
  4. 《Maven官方指南》可选的依赖和依赖排除
  5. 《Maven官方文档》什么是原型(Archrtype)?
  6. spring boot集成mongodb最简单版
  7. Maven入门指南(二)
  8. Apache Storm 官方文档 —— 使用 Maven 构建 Storm 应用
  9. Spring Boot 集成 FreeMarker 详解案例
  10. Springboot 整合 Dubbo/ZooKeeper 详解 SOA 案例
  11. 《KAFKA官方文档》5.2 APIs
  12. Maven的Java插件开发指南
  13. Maven入门指南(一)
  14. Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
  15. Spring Boot 整合 Redis 实现缓存操做
相关文章
相关标签/搜索