Apache Maven(四):依赖
依赖管理是Maven的特性之一,它是用户最为熟悉的特性之一,也是Maven擅长的领域之一。管理单个项目的依赖并没有太大困难,但是当您开始处理由数十或数百个模块组成的多模块项目和应用程序时,Maven可以帮助您在维护高控制程度和稳定性。
依赖的传递性
依赖的传递性是Maven 2.0中的一项新功能,这样可以避免需要去指定自己的依赖关系需要的库,并自动包含它们。简单来说,就是你从远程库中下载你所依赖的项目,这些项目所依赖的项目你也可以在项目中使用。项目从父项或从属关系中继承的任何项目都可以使用。
以下几种会限制依赖的传递性:
- 依赖调解: 这决定遇到多个工件版本时使用哪一个依赖的版本。在Maven 2.0只支持使用“最近原则”,最近原则使用依赖关系树中项目最近的版本。例如我们定义A->B->C->D2.0 和 A->E->D1.0,因为A到E到D1.0的路径最短,根据最近原则。则构建A项目时使用D1.0,你可以直接在A项目中添加D2.0的依赖直接强制使用D2.0。注意如果依赖深度树处于相同深度时,直到Maven 2.0.9才使用依赖声明的顺序,谁优先在A项目中声明依赖就使用谁包含的依赖项目。
- 依赖关系管理: 这允许项目的作者直接指定在项目中遇到的工件版本。在上一节示例中,依赖D直接被添加到A项目中,即使A不直接使用D。A可以直接在依赖管理(<dependencyManagement>标签)中直接添加依赖D强制使用哪一个版本。
- 依赖的范围: 这允许您只包含适合当前构建阶段的依赖关系。
- 排除依赖关系: 如果项目X依赖于项目Y,而项目Y依赖于项目Z,则项目X的所有者可以使用“排除”元素将项目Z显式排除为依赖项。
- 可选依赖关系: 如果项目Y依赖于项目Z,则项目Y的所有者可以使用“可选”元素将项目Z标记为可选依赖项。当项目X依赖于项目Y时,X将仅依赖于Y而不依赖于Y的可选依赖项Z.项目X的所有者可以根据她的选项明确添加对Z的依赖项。(将可选依赖关系视为“默认排除”可能会有所帮助。)
依赖范围
依赖范围可以用于限制依赖的传递性,也影响构建任务的类路径。包含如下6个可用范围:
- compile:这是默认范围,如果没有指定依赖范围,则使用该范围。compile的依赖关系在项目的所有类路径中可以使用,而且会传播到所依赖的项目。
- provided: 这个与compile非常相似,但是JDK或容器运行期的依赖。例如可以将Servlet API设置为该选项,因为Web容器提供了这些类,此范围只需在编译和测试类路径中使用,且不具有传递性。
- runtime: 此范围编译时不使用,运行期使用。它在运行时和测试类路径中,但不在编译类路径中。
- test: 此范围表示正常使用应用程序中不需要使用的依赖,并且只适用于测试编译和执行阶段。不具有传递性。
- system: 该范围与provided范围类似,只是必须明确提供包含的JAR。工件始终可以使用,并且不会在存储库中查找。
- import: 仅在Maven 2.0.9以后版本中使用,此范围仅可在<dependencyManagement>标签中使用。它表示用指定POM的<dependencyManagement>部分中的依赖项替换掉依赖项。由于它们被替换,因此具有import范围的依赖关系实际关系并不参与传递性。
依赖管理
依赖管理是集中依赖信息的机制。当你有一组继承公共父项目的项目时,可以将所有依赖项信息放入到公共POM中,并且可以更简单的引用于子POM中。通过下面的例子可以很好的说明,鉴于这两个POM拓展同一个父POM:
项目A:
1 <project> 2 ... 3 <dependencies> 4 <dependency> 5 <groupId>group-a</groupId> 6 <artifactId>artifact-a</artifactId> 7 <version>1.0</version> 8 <exclusions> 9 <exclusion> 10 <groupId>group-c</groupId> 11 <artifactId>excluded-artifact</artifactId> 12 </exclusion> 13 </exclusions> 14 </dependency> 15 <dependency> 16 <groupId>group-a</groupId> 17 <artifactId>artifact-b</artifactId> 18 <version>1.0</version> 19 <type>bar</type> 20 <scope>runtime</scope> 21 </dependency> 22 </dependencies> 23 </project>
项目B:
1 <project> 2 ... 3 <dependencies> 4 <dependency> 5 <groupId>group-c</groupId> 6 <artifactId>artifact-b</artifactId> 7 <version>1.0</version> 8 <type>war</type> 9 <scope>runtime</scope> 10 </dependency> 11 <dependency> 12 <groupId>group-a</groupId> 13 <artifactId>artifact-b</artifactId> 14 <version>1.0</version> 15 <type>bar</type> 16 <scope>runtime</scope> 17 </dependency> 18 </dependencies> 19 </project>
这两个示例中的POM共享一个共同的依赖关系,并且每个都有一个不重要的依赖关系,这些信息可以像如下示例放在父POM中:
1 <project> 2 ... 3 <dependencyManagement> 4 <dependencies> 5 <dependency> 6 <groupId>group-a</groupId> 7 <artifactId>artifact-a</artifactId> 8 <version>1.0</version> 9 10 <exclusions> 11 <exclusion> 12 <groupId>group-c</groupId> 13 <artifactId>excluded-artifact</artifactId> 14 </exclusion> 15 </exclusions> 16 17 </dependency> 18 19 <dependency> 20 <groupId>group-c</groupId> 21 <artifactId>artifact-b</artifactId> 22 <version>1.0</version> 23 <type>war</type> 24 <scope>runtime</scope> 25 </dependency> 26 27 <dependency> 28 <groupId>group-a</groupId> 29 <artifactId>artifact-b</artifactId> 30 <version>1.0</version> 31 <type>bar</type> 32 <scope>runtime</scope> 33 </dependency> 34 </dependencies> 35 </dependencyManagement> 36 </project>
然后这两个子POM就变得更简单:
精简后的项目A:
1 <project> 2 ... 3 <dependencies> 4 <dependency> 5 <groupId>group-a</groupId> 6 <artifactId>artifact-a</artifactId> 7 </dependency> 8 9 <dependency> 10 <groupId>group-a</groupId> 11 <artifactId>artifact-b</artifactId> 12 <!-- This is not a jar dependency, so we must specify type. --> 13 <type>bar</type> 14 </dependency> 15 </dependencies> 16 </project>
精简后的项目B:
1 <project> 2 ... 3 <dependencies> 4 <dependency> 5 <groupId>group-c</groupId> 6 <artifactId>artifact-b</artifactId> 7 <!-- This is not a jar dependency, so we must specify type. --> 8 <type>war</type> 9 </dependency> 10 11 <dependency> 12 <groupId>group-a</groupId> 13 <artifactId>artifact-b</artifactId> 14 <!-- This is not a jar dependency, so we must specify type. --> 15 <type>bar</type> 16 </dependency> 17 </dependencies> 18 </project>
注意:在这两个依赖关系引用中,我们必须指定<type/>元素,因为dependencyManagement最小信息集实际上是 {groupId,artifactId,type,classifier}。在大多数情况下,这些依赖关系引用没有classifier,这允许我将标识设置为{groupId,artifactId},因为type默认为jar,classifier默认为null。
导入依赖
导入依赖是在Maven 2.0.9中引入的,所以必须使用该版本或更高版本才能使用导入依赖。因为在项目中只能从单个父继承,在较大项目中可能无法完成某些任务,于是项目可以引入导入托管依赖项,只需要在POM中设置dependency的scope为import即可。如下示例:
项目B:
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>maven</groupId> 4 <artifactId>B</artifactId> 5 <packaging>pom</packaging> 6 <name>B</name> 7 <version>1.0</version> 8 <dependencyManagement> 9 <dependencies> 10 <dependency> 11 <groupId>maven</groupId> 12 <artifactId>A</artifactId> 13 <version>1.0</version> 14 <type>pom</type> 15 <scope>import</scope> 16 </dependency> 17 <dependency> 18 <groupId>test</groupId> 19 <artifactId>d</artifactId> 20 <version>1.0</version> 21 </dependency> 22 </dependencies> 23 </dependencyManagement> 24 <dependencies> 25 <dependency> 26 <groupId>test</groupId> 27 <artifactId>a</artifactId> 28 <version>1.0</version> 29 <scope>runtime</scope> 30 </dependency> 31 <dependency> 32 <groupId>test</groupId> 33 <artifactId>c</artifactId> 34 <scope>runtime</scope> 35 </dependency> 36 </dependencies> 37 </project>
上面的示例,B项目将会导入A项目的所有依赖,除了d,因为d项目它是在pom中定义的。
项目X:
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>maven</groupId> 4 <artifactId>X</artifactId> 5 <packaging>pom</packaging> 6 <name>X</name> 7 <version>1.0</version> 8 <dependencyManagement> 9 <dependencies> 10 <dependency> 11 <groupId>test</groupId> 12 <artifactId>a</artifactId> 13 <version>1.1</version> 14 </dependency> 15 <dependency> 16 <groupId>test</groupId> 17 <artifactId>b</artifactId> 18 <version>1.0</version> 19 <scope>compile</scope> 20 </dependency> 21 </dependencies> 22 </dependencyManagement> 23 </project>
项目Y:
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>maven</groupId> 4 <artifactId>Y</artifactId> 5 <packaging>pom</packaging> 6 <name>Y</name> 7 <version>1.0</version> 8 <dependencyManagement> 9 <dependencies> 10 <dependency> 11 <groupId>test</groupId> 12 <artifactId>a</artifactId> 13 <version>1.2</version> 14 </dependency> 15 <dependency> 16 <groupId>test</groupId> 17 <artifactId>c</artifactId> 18 <version>1.0</version> 19 <scope>compile</scope> 20 </dependency> 21 </dependencies> 22 </dependencyManagement> 23 </project>
项目Z:
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>maven</groupId> 4 <artifactId>Z</artifactId> 5 <packaging>pom</packaging> 6 <name>Z</name> 7 <version>1.0</version> 8 <dependencyManagement> 9 <dependencies> 10 <dependency> 11 <groupId>maven</groupId> 12 <artifactId>X</artifactId> 13 <version>1.0</version> 14 <type>pom</type> 15 <scope>import</scope> 16 </dependency> 17 <dependency> 18 <groupId>maven</groupId> 19 <artifactId>Y</artifactId> 20 <version>1.0</version> 21 <type>pom</type> 22 <scope>import</scope> 23 </dependency> 24 </dependencies> 25 </dependencyManagement> 26 </project>
在上面的示例中,项目Z从X和Y中导入依赖,但是,X和Y都包含了依赖项a,在这里,a版本的1.1将被导入,因为X比Y先声明,而a没有在Z的dependencyManagement中声明。
这个过程是递归的。例如,如果X导入另一个Q,则在处理Z时,它将显示Q中所有的依赖项。