标签:maven
软件设计人员往往会采用各种方式对软件划分模块,以得到更清晰的设计及更高的重用性。当把Maven应用到实际项目中的时候,也需要将项目分成不同模块。Maven的聚合特性能够把项目的各个模块聚合在一起构建,而Maven的继承特性则能帮助抽取各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性。
<maven实战>一书实现了两个模块account-email和account-persist用以实现一个用户注册的项目。 在本文中,也使用这个例子来讲述聚合与继承。
聚合:
由于有account-email和account-persist模块的存在,这时就有一个需求冒出来了:我们会想要一次构建两个项目,而不是到两个模块的目录下分别执行mvn命令。Maven聚合(或者称为多模块)这一特性就是为该需求服务的。
我们需要额外创建一个名为account-aggregator的模块,然后通过该模块构建整个项目的所有模块。account-aggregator本身作为一个Maven项目,它有自己POM,不过作为一个聚合项目,其POM又有特殊的地方。如下为account-aggregator的pom.xml内容。
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-aggregator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Aggregator</name>
<modules>
<module>account-email</module>
<module>account-persist</module>
<modules>
</project>
需要注意的是,对于聚合模块来说,其打包方式packaging的值必须为pom,否则就无法构建。
<modules>元素是实现聚合的最核心的配置,用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合。这里每个module的值都是一个当前POM的相对目录。
为了方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在,这样当用户得到源码的时候,第一眼发现的就是聚合模块的POM,不用从多个模块中去寻找聚合模块来构建整个项目。
account-aggregator没有src/main/java、src/test/java等目录,因为聚合模块仅仅是帮助聚合其他模块构建的工具,它本身并无实质的内容。
我们可以在聚合模块下运行mvn clean install命令查看聚合是怎样进行模块的构建的。
可以看到,Maven会首先解析聚合模块POM、分析要构建的模块、并计算出一个反应堆构建顺序(Reactor Build Order),然后根据这个顺序依次构建各个模块。反应堆是所有模块组成的一个构建结构。后面会详细讲解Maven的反应堆。
继承:
面向对象设计中,程序员可以建立一种类的父子结构,然后在父类中声明一些字段和方法供子类继承,这样就可以做到"一处声明,多处使用"。类似的,我们需要创建父子结构,然后在父POM中声明一些配置供子POM继承,以实现"一处声明,多处使用"的目的。
父模块的POM与聚合模块的POM内容相似。作为父模块POM,其打包类型也必须为pom。
由于父模块只是为了帮助消除配置的重复,因此它本身不包含除POM之外的项目文件,也就不需要src/main/java之类的文件夹了。
在子模块的pom中,使用parent元素声明父模块,parent下的子元素groupId、artifactId和version指定了父模块的坐标,这三个元素是必须的。元素relativePath表示父模块POM的相对路径。当项目构建时,Maven会首先根据relativePath检查父POM,如果找不到,再从本地仓库查找。relativePath的默认值是../pom.xml,也就是说,Maven默认父POM在上一层目录下。
可继承的POM元素:
父POM的一些元素可以被子POM所继承。以下简单说明:
- groupId:项目组ID,项目坐标的核心元素
- version:项目版本,项目坐标的核心因素
- description:项目的描述信息
- organization:项目的组织信息
- inceptionYear:项目的创始年份
- url:项目的URL地址
- developers:项目的开发者信息
- contributors:项目的贡献者信息
- distributionManagement:项目的部署配置
- issueManagement:项目的缺陷跟踪系统信息
- ciManagement:项目的持续集成系统信息
- scm:项目的版本控制系统西溪
- malilingLists:项目的邮件列表信息
- properties:自定义的Maven属性
- dependencies:项目的依赖配置
- dependencyManagement:项目的依赖管理配置
- repositories:项目的仓库配置
- build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等
- reporting:包括项目的报告输出目录配置、报告插件配置等
依赖管理:
Maven提供的denpencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。
父模块在dependencyManagement中声明依赖,子模块会继承父模块中的dependencyManagement配置,完整的依赖声明已经包含在父POM中,子POM只需要配置简单的groupId和artifactId就能获得对应的依赖信息,从而引入正确的依赖。
使用这种依赖机制虽然不能减少太多的POM的配置,但是通过dependencyManagement声明依赖能够统一项目范围中依赖的版本,当依赖版本在父POM中声明之后,子模块在使用依赖的时候就无须声明版本,也就不会发生多个子模块使用依赖版本不一致的情况。这可以帮助降低依赖冲突的几率。
如果子模块不声明依赖的使用,即使该依赖已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果。
import依赖范围:
import依赖范围需要在dependencyManagement元素下使用才有效果,使用该范围的依赖通常指向一个POM,作用是将目标POM中dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。例如我们想要在一个模块中使用与另一个模块完全一样的dependencyManagement配置,除了复制配置或者继承这两者方式之外,还可以使用import范围依赖将这一配置导入。代码如下:
<dependencyManagemen>
<dependencies>
<denpendency>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
<denpendency>
<dependencies>
<dependencyManagement>
import范围依赖由于其特殊性,一般都是指向打包类型为pom的模块。如果有多个项目,它们使用的依赖版本都是一致的,则就可以定义一个使用dependencyManagement专门管理依赖的POM,然后在各个项目中导入这些依赖管理配置。
插件管理:
Maven也提供了pluginManagement元素帮助管理插件,插件管理的使用与依赖管理的使用方式类似。
如果子模块需要不同的插件配置,则可以自行配置以覆盖父模块的pluginManagement配置。
当项目中的多个模块有同样的插件配置时,应当将配置移到父POM的pluginManagement元素中。即使各个模块对于同一插件的具体配置不尽相同,也应当使用父POM的pluginManagement元素统一声明插件的版本。甚至可以要求将所有用到的插件的版本在父POM的pluginManagement元素中声明,子模块使用插件时不配置版本信息,这样做可以统一项目的插件版本,避免潜在的插件不一致或者不稳定的情况,也更易于维护。
聚合与继承的关系:
在实际的项目中,我们通常把聚合模块和父模块,放在一起实现。
反应堆:
在一个多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一个构建结构。对于单模块的项目,反应堆就是该模块本身,但对于多模块的项目来说,反应堆就包含了各模块之间继承与依赖的关系,从而能够自动计算出合理的模块构建顺序。
反应堆的构建顺序:
实际的构建顺序是这样形成的:Maven按序读取POM,如果该POM没有依赖模块,那么就构件该模块,否则就先构建其依赖模块,如果该依赖还依赖于其他模块,则进一步先构建依赖的依赖。
模块间的依赖关系会将反应堆构成一个有向非循环图(DAG),各个模块是该图的节点,依赖关系构成了有向边。这个图不允许出现循环,因此,当出现模块A依赖于B,而B又依赖于A的情况时,Maven就会报错。
Maven学习笔记(七):聚合与继承
标签:maven
原文地址:http://blog.csdn.net/troy__/article/details/39085611