依赖、聚合和继承
依赖
我们项目中依赖的 Jar 包可以通过依赖的方式引入,通过在 dependencies 元素下添加 dependency 子元素,可以声明一个或多个依赖。通过控制依赖的范围,可以指定该依赖在什么阶段有效。Maven 的几种依赖范围:
传递依赖
依赖具有传递性,例如 Project A 依赖于 Project B,B 依赖于 C,则 B 对 C 的依赖关系也会传递给 A。
如果我们的项目引用了一个jar包,而该jar包又引用了其他jar包。那么,
在默认情况下,项目编译时, Maven会把直接引用和间接引用的jar包都
下载到本地( ~/.m2/repository )。
如果我们不需要这种传递性依赖,可用 <optional> 去除这种依赖的传递,如
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> <optional>true<optional> </dependency>
假设第三方的 jar 包中没有使用 <optional>
来去除某些依赖的传递性,那么可以在当前的 POM 文件中使用 <exclusions>
元素声明排除依赖,exclusions 可以包含一个或者多个 exclusion 子元素,因此可以排除一个或者多个传递性依赖。如
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency>
依赖冲突
若项目中多个jar同时引用了相同的jar时,会产生依赖冲突,但Maven采用了两种
避免冲突的策略,因此在Maven中是不存在依赖冲突:
- 短路优先
本项目 =》 A.jar =》B.jar =》 X.jar
本项目=》 C.jar =》 Xjar - 声明优先
若引用路径长度相同时,在pom.xmI中谁先被声明,就使用谁
多模块项目 / 聚合
现实中一个项目往往是由多个 project 构成的,在进行构建时,我们当然不想针对多个 project 分别执行多次构建命令,这样极容易产生遗漏也会大大降低效率。Maven 的聚合功能可以通过一个父模块将所有的要构建模块整合起来。
➢ 父模块pom文件的配置: packaging 类型必须是pom
<groupId>com.java.ad</ groupId> <artifactId>java-ad</artifactId> <packaging>pom</ packaging> <version>1.O-SNAPSHOT</version>
➢聚合子模块:使用modules标签
<modules> <module>ad-eureka</module> <module>java-ad-service</module> <module>ad-gateway</module> </modules>
将父模块的打包类型声明为 POM,通过 <modules>
将各模块集中到父 POM 中。如下其中 <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 元素都可以被继承,如下是可继承的元素列表。
可继承元素列表
名称 描述
groupId 项目组 ID
version 项目版本
description 描述信息
organization 组织信息
inceptionYear 创始年份
url 项目的 url 地址
developers 开发者
contributors 贡献者信息
distributionManagerment 部署信息
issueManagement 缺陷跟踪系统
ciManagement 持续继承信息
scm 版本控制信息
mailingList 邮件列表信息
properties 自定义的属性
dependencies 依赖配置
dependencyManagement 依赖管理配置
repositories 仓库配置
build 源码目录,插件管理等配置
reporting 报告配置
排除依赖
如果我们只想下载直接引用的jar包,那么需要在pom.xml 中做如下配置(给出需要排除的坐标)
Maven 属性
在 POM 文件中常常需要引用已定义的属性以降低代码的冗余,提高代码的可重用性,这样不仅能降低代码升级的工作量也能提高代码的正确率。有些属性是用户自定义的,有些属性是可以直接引用的已定义变量。
Maven 的可用属性类型可分为 5 种,它们分别是:
内置属性。这种属性跟 Maven Project 自身有关,比如要引入当前 Project 的版本信 息,那么只需要在使用的位置引用 ${version} 就行了。
Setting 属性。上文中已经提到 Maven 自身有一个 settings.xml 配置文件,它里面含有包括仓库,代理服务器等一些配置信息,利用 ${settings.somename} 就可以得到文件里相应元素的值。
POM 属性。这种属性对应 POM 文件中对应元素的值,例如 p r o j e c t . g r o u p I d 对 应 了 < g r o u p I d > < / g r o u p I d > 中 的 值 , {project.groupId} 对应了 <groupId></groupId> 中的值,project.groupId对应了<groupId></groupId>中的值,{project.artifactId} 对应了 </ artifactId > 中的值。
系统环境变量。可以使用 env.${name} 来获得相应 name 对应的环境变量的值,例如 ${env.JAVA_HOME} 得到的就是 JAVA_HOME 的环境变量值。
用户自定义变量。这种类型的变量是使用最频繁和广泛的变量,完全由用户自己定义。在 POM 文件中加入 元素并将自定义属性作为其子元素。格式如
<properties> <path>../../sourcecode</path> </properties>
Maven 3 特性
Maven 3 在性能和灵活性方面都比 Maven2 有了很大提升,它的新特性总结起来有以下几点:
兼容低版本 Maven,也就是向后兼容,因此用户可以将 Maven2 的项目移植到 Maven3 上来。
性能优化。CPU 利用率更高,内存消耗更小,经过优化的 Maven3 比 Maven2 构建速度快出 50% 以上,这对于构建大型项目的开发者来说无疑会节省大量的时间。
在早先的版本中,开发者必须在子模块中指定父版本,当进行代码的迁移或升级时,这会带来额外的维护工作,Maven3.1 将会消除在子模块上指定父版本的需要。
4.Maven3 改善了错误报告,它会在错误报告中提供指向 Maven Wiki 页面的链接,这样开发者可以方便的查看更全面的错误描述和可能的原因。
增加了 Maven Shell,通常我们可以在系统自带的 console 里执行 Maven 命令,但是通过自安装的 Maven Shell 可以提高生成速度,它是一个是 Maven 的命令行接口工具,可以缓存解析过的 POM,避免了重复调用 Maven 的启动成本。Maven Shell 不属于 Maven 发行包的一部分,需要单独下载。
M2Eclipse 实现了 Maven 和 Eclipse 的集成,与一个使用更广泛的 IDE 进行集成从而为开发者带来的便利是不言而喻的。
总结
Maven 有着许多实用的特点,它使用了标准的目录结构和部署。这就使得开发人员能够适应不同的项目,并且不用学习任何结构方面新的东西,也不用掌握特殊的指令来构建结构。当然,Maven 的使用还不够普及,相信随着时间的推移,它的功能会更完善,使用的人群也会越来越广泛。