传递性依赖
传递性依赖是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的依赖。(你可以把可选依赖理解为默认排除)。
依赖范围
Maven有以下依赖范围:
- compile:编译依赖范围,使用此依赖范围对于编译、测试、运行三种classpath都有效,即在编译、测试和运行时都要使用该依赖jar包;
- test:测试依赖范围,只对测试有效,表明只在测试的时候需要,在编译和运行时将无法使用该类依赖,如 junit;
- provided:已提供依赖范围。编译和测试有效,运行无效。如servlet-api,在项目运行时,tomcat等容器已经提供,无需Maven重复引入;
- runtime:运行时依赖范围。测试和运行有效,编译无效。如 jdbc 驱动实现,编译时只需接口,测试或运行时才需要具体的 jdbc 驱动实现;
- system:系统依赖范围,使用system范围的依赖时必须通过systemPath元素显示地指定依赖文件的路径,不依赖Maven仓库解析,所以可能会造成建构的不可移植,谨慎使用。
聚合
现实中一个项目往往是由多个 project 构成的,在进行构建时,我们当然不想针对多个 project 分别执行多次构建命令,这样极容易产生遗漏也会大大降低效率。Maven 的聚合功能可以通过一个父模块将所有的要构建模块整合起来,将父模块的打包类型声明为 POM,通过 <modules> 将各模块集中到父 POM 中。如清单 7,其中 <module></module> 中间的内容为子模块工程名的相对路径。
<modules>
<module>../com.dugeng.project1</module>
<module>../com.dugeng.project2</module>
</modules>
父类型的模块,不需要有源代码和资源文件,也就是说,没有 src/main/java 和 src/test/java 目录。Maven 会首先解析聚合模块的 POM 文件,分析要构建的模块,并通过各模块的依赖关系计算出模块的执行顺序,根据这个潜在的关系依次构建模块。将各子模块聚合到父模块中后,我们就可以对父模块进行一次构建命令来完成全部模块的构建。
继承
在面向对象的编程中我们学会了继承的概念,继承是可重用行即消除重复编码的行为。Maven 中继承的用意和面向对象编程中是一致的。与聚合的实现类似,我们通过构建父模块将子模块共用的依赖,插件等进行统一声明,在聚合和继承同时使用时,我们可以用同一个父模块来完成这两个功能。
例如将 com.dugeng.parent 这个模块声明为 project1 和 project2 的父模块,那么我们在 project1 和 2 中用如下代码声明父子关系,
<parent>
<groupId>com.dugeng.mavenproject</groupId>
<artifactId>com.dugeng.parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../com.dugeng.parent/pom.xml</relativePath>
</parent>
由于父模块只是用来声明一些可共用的配置和插件信息,所以它也像聚合模块一样只需要包括一个 POM 文件,其它的项目文件如 src/main/java 是不需要的。
聚合和继承存在一些共性和潜在的联系,在实际的应用中,经常将聚合模块的父模块和继承的父模块定义为同一个。
并不是所有的 POM 元素都可以被继承,表 3 是一个可继承的元素列表。