Maven 聚合与继承java
1. 背景spring
在这个技术发展飞速的时代,各种用户对软件的要求愈来愈高,软件自己也变得越apache
来越复杂。所以,软件设计人员每每会采用各类方式对软件划分模块,以获得更清晰的maven
设计及更高的重用性。性能
Maven 的聚合特性可以把项目的各个模块聚合在一块儿构建,而 Maven 的继承特性测试
则能帮助抽取各模块相同的依赖和插件等配置,在简化 pom 配置的同时,还能促进各个ui
模块配置的一致性。url
2. 聚合插件
到目前为止,咱们都是一个项目一个项目的构建。一个简单的需求就出来了,咱们命令行
会想要一此构建两个项目,而不是到两个模块分别执行 mvn 命令,Maven 聚合(或者
称为多模块)这一特性就是为该需求服务的。
a) 建立聚合模块
须要建立一个新的模块,来聚合构建项目中其余全部模块。
<modelVersion>4.0.0</modelVersion><groupId>org.lichee</groupId><artifactId>lichee</artifactId><version>1.0.0</version><packaging>pom</packaging><name>lichee :: project</name><description>This is a project demo of lynch</description><inceptionYear>2013-2014</inceptionYear><modules> <module>parent</module> <module>core</module> <module>common</module> <module>support</module> <module>test</module> <module>simple-example</module></modules>
b) 聚合模块特征
i. Maven 项目
聚合模块自己也是一个 Maven 项目。
ii. 目录差别
准确说除了其余模块目录和 pom.xml,没有其余任何 src、test、target 等相关目录。
iii. pom.xml
1) 相同的 groupId、version
2) packaging 必须是 pom
继承父模块也同样必须是 pom。
3) name 提供一个相对容易阅读的名字
配置合理的 name 字段,会让 Maven 的构建输出更清晰。
4) modules
实现聚合的最核心配置,用户能够经过一个打包方式为 pom 的 Maven 项目中声明,
任意数量的 module 元素来实现模块的聚合。每一个 module 的值都是一个当前 pom 的相对目录。
5) 模块所处的目录名称与其 artifactId 一致
文件的目录名称和 artifactId 相同,但不绝对,只是 module 里面的配置,
必须和文件目录上的名字同样。
6) 推荐目录格式
一般将聚合模块放在项目目录的最顶层,其余模块则做为聚合模块的子目录存在,
方便用户寻找聚合模块来构建整个项目。
c) 解析过程
Maven 会首先解析聚合模块的 pom、分析要构建的模块、并计算出一个反应堆构建顺序
(Reactor Build Order),而后根据这个顺序依次构建各个模块。
i. 项目构建小结报告
ii. 各个模块构建成功与否、花费时间
iii. 整个构建花费的时间、使用内存等
3. 继承
不少项目中,不一样的子模块有着相同的 groupId、version,相同的 spring、junit等依赖,
相同的 maven-compiler-plugin 插件等配置。这就是重复,重复意味着更多的劳动和更多的潜在问题。
在 Maven 世界中,有机制能让咱们抽取出重复的配置,这就是 pom 的继承。
a) 建立继承模块
咱们须要建立 pom 的父子结构,在父 pom 中声明一些配置供子 pom 继承,一处声明,多处使用。
父模块:
<groupId>org.lichee</groupId><artifactId>lichee-parent</artifactId><version>1.0.0</version><packaging>pom</packaging><name>lichee :: parent</name>
子模块:
<parent><groupId>org.lichee</groupId><artifactId>lichee-parent</artifactId><version>1.0.0</version><relativePath>../parent/</relativePath></parent><artifactId>lichee-core</artifactId><packaging>jar</packaging><name>lichee :: core</name>
b) 继承模块特征
i. Maven 项目
继承模块自己也是一个 Maven 项目。
ii. 目录差别
准确说除了 pom.xml,没有其余任何 src、test、target 等相关目录。
iii. pom.xml
1) groupId、version
虽然子模块没有声明,不过不表明子模块没有这两个属性,隐式地从父模块继承了这两个元素,
这也就消除一些没必要要的配置。若是子模块须要使用和父模块,
不同的 groupId 或者 version 的状况,那么用户彻底能够在子模块中显式声明。
对于 artifactId 元素来讲,子模块应该显式声明。
2) packaging 必须是 pom
和聚合模块同样。
3) name 提供一个相对容易阅读的名字
配置合理的 name 字段,会让 Maven 的构建输出更清晰。
4) relativePath
表示父模块 pom 的相对路径,默认值为../pom.xml,也就是说,
Maven默认父 pom 在上一层目录下。若是子模块没有设置正确的 relativePath,
Maven 将没法找到父 pom,这将直接致使构建失败。
4. 可继承的 pom 元素
a) groupId
项目组 Id,项目坐标的核心元素。
b) version
项目版本,项目坐标的核心元素。
c) description
项目的描述信息。
d) organization
项目的组织信息。
e) inceptionYear
项目的创始年份。
f) url
项目的 url 地址。
g) developers
项目的开发者信息。
h) contributors
项目的贡献者信息。
i) distributionManagement
项目的部署配置。
j) issueManagement
项目的缺陷跟踪系统信息。
k) ciManagement
项目的持续集成系统信息。
l) scm
项目的版本控制系统信息。
m) mailingLists
项目的邮件信息列表。
n) properties
自定义的 Maven 属性。
o) dependencies
项目的依赖配置。
p) dependencyManagement
项目的依赖管理配置。
q) repositories
项目的仓库配置。
r) build
包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等。
s) reporting
包括项目的报告输出目录配置、报告插件配置等。
5. 依赖管理
经过 dependencies 元素,能够将父模块的依赖继承到子类中,但并非每个子模块
都须要相同的依赖。也不合理。
a) dependencyManagement
让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。
在这个元素下的依赖声明不会引入实际的依赖,不过它可以约束 dependencies 下的依赖使用。
父模块:
<properties> <junit.version>4.11</junit.version> <spring.version>3.2.5.RELEASE</spring.version> <jdk.version>1.6</jdk.version></properties><dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> </dependencies></dependencyManagement>
子模块:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency></dependencies>
i. 变量提取
spring 和 junit 依赖的版本以 maven 变量的形式提取出来,不只消除了一些重复,
也使得个依赖的版本处于更加明显的位置。
ii. 引入时机
dependencyManagement 声明的依赖不会被任何一个子模块引入,
不过这段配置是会被子模块继承的。子模块的引入这些依赖就很简单了。
1) 只配置 groupId 和 artifactId
省去 version 和 scope,这些想信息能够被省略是由于子模块继承了,
父模块的 dependencyManagement,完整的依赖声明在父 pom 中,
子模块只须要配置简单的 groupId 和 artifactId 就能得到对应的依赖信息,
从而引入正确的依赖。
2) 选择性引入依赖
若是子模块的 pom 中不声明某个依赖的使用,即便该依赖已经在父 pom
的 dependencyManagement 中声明了,也不会产生任何实际的效果,
也就是说不会被引入到子模块的依赖中。
3) 最佳推荐
这种依赖管理机制彷佛不能减小太多的 pom 配置,不过仍是推荐采用这种方式。
由于在父 pom 中使用 dependencyManagement 声明依赖可以统一项目范围中的依赖版本,
子模块在使用过程当中无需声明版本,也不会发生多个子模块使用的依赖版本不一致,
这能够下降依赖冲突的概率,对于后期升级或者修改依赖版本,也是提供了大大的便利。
4) import 依赖范围
这个范围的依赖只在 dependencyManagement 元素下才有效果,使用该范围的依赖一般
指向一个 pom,做用是将目标 pom 中的dependencyManagement 配置导入并合并到,
当前 pom 的dependencyManagement 元素中。import 范围依赖因为其特俗性,
通常都是指向打包类型为 pom 的模块。
6. 插件管理
Maven 提供了 dependencyManagement 元素帮助管理依赖,相似的,
Maven 也提供了 pluginManagement 元素帮助管理插件。
a) pluginManagement
该元素中配置的依赖插件不会形成实际插件的行为,当子模块 pom 中配置了真正的plugin 元素,
而且 groupId、artifactId 与 pluginManagement 中配置的插件匹配时,
pluginManagement 的配置才会影响实际的插件行为。
父模块:
<pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.6</source> <target>1.6</target> <showWarnings>true</showWarnings> </configuration> </plugin> </plugins></pluginManagement>
子模块:
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin></plugins>
i. 变量提取
和 dependencyManagement 同样。
ii. 引入时机
和 dependencyManagement 同样。
iii. 最佳推荐
当项目中的多个模块有一样的插件配置时,应当将配置移到父 pom 的pluginManagement 元素中。
甚至能够要求将全部用到的插件的版本在父pom的pluginManagement元素中声明,
子模块使用插件时不配置版本信息,这么作能够统一插件的版本,
避免潜在的插件不一致或者不稳定问题,也便易于维护。
7. 聚合与继承的关系
a) 相同点
聚合 pom 和继承关系中的父 pom 的 packaging 都必须是 pom,
聚合模块与继承关系中的父模块除了 pom 以外都没有实际的内容。
每每也会发现,一个 pom 既是聚合 pom,也是父 pom,这么作主要是为了方便,
融合使用聚合与继承也没有什么问题。
b) 不一样点
i. 聚合
为了方便快速构建项目。它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块。
ii. 继承
为了消除重复配置。它不知道有哪些子模块继承与它,但那些子模块都必须知道,
本身的父 pom 是什么。
c) 如图所示
8. 约定优于配置
Maven 提倡“约定优于配置”(Convention Over Configuration),这是 Maven最核心的设计理念之一。
缘由之一就是使用约定能够大量减小配置。
a) 源码目录为 src/main/java/
b) 源码资源目录为 src/main/resources/
c) 测试目录为 src/test/java/
d) 测试资源目录为 src/test/resources/
e) 编译输出目录为 target/classes/
f) 打包方式为 jar
g) 包输出目录为 target/
h) 超级 pom
超级 pom 定义以上的目录结构、核心插件设定版本。Maven 设定核心插件的缘由,
是防止因为插件版本的变化而形成构建的不稳定。
遵循约定虽然损失了必定的灵活性,用户不能随意安排目录结构,可是却能减小配置。
更重要的是,遵循约定可以帮用户遵循构建标准。个性每每意味着牺牲通用性,
意味着增长无谓的复杂度。
9. 反应堆
反应堆(Reactor)是指全部模块组成的一个构建结构。对于单模块的项目,反应堆就
是该模版自己,但对于多模块项目来讲,反应堆就包含了各模块之间继承与依赖的关系。
a) 反应堆的构建顺序
通常状况按照 modules 的声明顺序,不过也有特俗状况。Maven 按序读取 pom,
若是该 pom 没有依赖模块,那么就构建该模块,不然就先构建其依赖模块,
若是该依赖还依赖于其余模块,则进一步先构建依赖的依赖。
i. 继承或者依赖
Maven 还须要考虑模块之间的继承和依赖关系。
ii. 有向非循环图
模块间的依赖关系会将反应堆构成一个有向非循环图。
b) 裁剪反应堆
用户会想要仅仅构建完整反应堆中的某些个模块。也就是用户须要实时地裁剪反应堆。
Maven 提供不少命令行选项支持裁剪反应堆,mvn –h 查看。
略(不经常使用)