目录一览:
聚合
继承
可继承的POM元素
依赖管理
插件管理
聚合与继承
约定优于配置
反应堆
聚合与继承概述:
Maven的聚合特性能够把各项目的各个模块聚合在一起构建;
Maven的继承特性能帮助抽取各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性。
-------------------------------------------------------
用到的项目 《Maven实战》(许晓斌 著) 一书中的一个用户登录注册的项目
-------------------------------------------------------
项目简介:用户注册服务,分为以下5个模块
account-web:包含与web相关的内容
account-service:系统核心,封装下层细节,对外暴露接口,Facade模式
account-persist:处理帐户信息持久化
account-captcha:处理验证码的key、图片的生成以及验证等
account-email:处理邮件服务的配置、激活邮件的编写和发送
-------------------------------------------------------
需求:一次构建这一个模块
创建一个account-aggregator的模块,然后通过该模块构建整个项目的所有模块
account-aggregator作为一个Maven项目,有自己的POM,同时也作为一个聚合项目
其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/xsd/maven-4.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> <module>account-captcha</module> <module>account-service</module> <module>account-web</module> </modules> </project>
name字段是为了给项目提供一个更容易阅读的名字
packaging对应默认值为jar,对于聚合模块来说,其打包方式packaging的值必须为pom,否则无法构建。
moudles元素,实现聚合的核心配置
每个moudle的值都是一个当前POM的相对目录,如:
account-aggregator的pom.xml的路径为:
D:/WorkStation/maven/account-aggregator/pom.xml,
那么其它模块如account-web就对应了目录:
D:/WorkStation/maven/account-aggregator/account-web
一般,为快速定位内容,模块所处的目录名称应当与其artifactId一致(推荐非必须)
为方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其它模块则作为聚合模块的子目录存在
聚合模块仅仅是帮助聚合其他模块的工具,它本身并无实质内容,因此聚合模块的内容仅是一个pom.xml文件
以上配置是聚合模块的父子目录结构:
account-aggregator --pom.xml --account-email ----src ----pom.xml --account-persist ----src ----pom.xml
对moudles作如下配置:
<modules> <module>../account-email</module> <module>../account-persist</module> <module>../account-captcha</module> <module>../account-service</module> <module>../account-web</module> </modules>
则对应聚合模块的平行目录结构:
account-aggregator --pom.xml account-email --src --pom.xml account-persist --src --pom.xml
测试接口而不测试实现原则,测试代码不能引用实现类
Maven的聚合特性可以通过一条命令同时构建N个模块,解决了多模块Maven项目的一个问题
问题:相同的配置、依赖。。。
POM的继承:通过继承机制抽取重复配置
一处声明多处使用
Java中,创建类的父子结构:父类中声明字段方法供子类POM继承
Maven中,创建POM的父子结构:父POM中声明一些配置供子POM继承
项目实例:
创建一个父模块account-parent,
在account-aggregator下创建一个名为account-parent的子目录,在该子目录下建立一个所有除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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Maven Account-Parent Project</name> <modules> <module>account-email</module> <module>account-persist</module> <module>account-captcha</module> <module>account-service</module> <module>account-web</module> </modules> </project>
注意:artifactId为account-parent,表示这是一个父模块
packaging为pom,作为父模块的POM,其打包类型也必须为pom
由于父模块只是为了帮助消除配置的重复,因此它本身不包含除POM之外的项目文件
其它模块来继承它,如account-email的POM(部分)如下:
<?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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../account-parent/pom.xml</relativePath> </parent> <artifactId>account-email</artifactId> <name>Maven Account-Email Project</name> <dependencies> 。。。 </dependencies> <build> <plugins> 。。。 </plugins> </build> </project>
parent元素声明父模块,其下的groupId、artifactId和version指定了你模块的坐标,这三个元素必须的
relativePath元素表示你模块POM的相对路径,上述配置中的../account-parent/pom.xml表示父POM的位置在与account-email目录平行的account-parent目录下
account-aggregator --pom.xml --account-parent ----pom.xml --account-email ----src ----pom.xml --account-persist ----src ----pom.xml
relativePath的默认值是../pom.xml,也就是说,Maven默认父POM在上一层目录下
正确设置relativePath很重要,因为相互具有继承关系
account-aggregator --pom.xml account-parent --pom.xml account-email --src --pom.xml account-persist --src --pom.xml
对于artifactId元素,子模块应该显式声明,一方面,如果完全继承groupId、artifactId和version,会造成坐标冲突,另一方面,好使使用不同的groupId或version,同样的artifactId容易造成混淆
最后将父模块account-parent加入到聚合模块account-aggregator中,在聚合模块中的pom.xml中加入
<module>account-parent</module>
总结:
1.聚合可以方便快速构建项目
2.继承可以避免重复配置
可继承的POM元素
groupId和version是可以被继承的,可以被继承的POM元素如下:
groupId
version
description
organization
inceptionYear
url
developers
contributors
distributionManagement:项目的部署配置
issueMagement
ciManagement
scm
mailingLists
properties
dependencies:项目的依赖配置
dependencyManagement
repositories
build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等
reporting
依赖管理
依赖是会被继承的
Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性
在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用,如在account-parent中加入dependencyManagement配置
<?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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Maven Account-Parent Project</name> <modules> <module>account-email</module> <module>account-persist</module> <module>account-captcha</module> <module>account-service</module> <module>account-web</module> </modules> <!-- Maven属性 --> <properties> <project.build.sourceEncoding> UTF-8 </project.build.sourceEncoding> <project.reporting.outputEncoding> UTF-8 </project.reporting.outputEncoding> <project.version>1.0.0-SNAPSHOT</project.version> <springframework.version>2.5.6</springframework.version> <junit.version>4.10</junit.version> </properties> <!-- 依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project>
这里使用dependencyManagement声明的依赖既不会给account-parent引入依赖,也不会给它的子模块引入依赖,不过这段配置是会被继承的。修改account-email的POM如下:
<?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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>account-email</artifactId> <name>Maven Account-Email Project</name> <properties> <javax.mail.version>1.4.1</javax.mail.version> <greenmail.version>1.3.1b</greenmail.version> </properties> <!-- 依赖 --> <dependencies> <!-- 继承父pom --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <!-- --> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>${javax.mail.version}</version> </dependency> <!-- 邮件服务测试 --> <dependency> <groupId>com.icegreen</groupId> <artifactId>greenmail</artifactId> <version>${greenmail.version}</version> <scope>test</scope> </dependency> </dependencies> </project>
这种依赖管理机制似乎不能减少太多POM配置,不过推荐这样做,原因保持版本一致,降低依赖冲突的机率
如果子模块不声明依赖的使用,即使该依赖已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果
import依赖范围:该范围的依赖只在dependencyManagement元素下才有效果,使用该范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement配置,除了复制配置或者继承这两种方式外,还可以使用import范围依赖将这一配置导入,如:
<dependencyManagement> <dependencies> <dependency> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
上述代码中依赖的type为pom,import范围依赖由于其特殊性,一般都是指向打包类型为pom的模块
如果有多个项目,它们使用的依赖版本都是一致的,则就可以定义一个使用dependencyManagement专门管理依赖的POM,然后在各个项目中导入这些依赖管理配置。
插件管理
Maven提供了pluginManagement元素管理插件,该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置都会影响实际的插件行为
如:在父POM中配置pluginManagement
<build> <!-- 插件 --> <pluginManagement> <plugins> <!-- 支持java 5 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> <!-- 使用UTF-8编码处理资源文件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.5</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </pluginManagement> </build>
继承了pluginManagement后的插件配置
<build> <plugins> <!-- 继承父pom --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> </plugin> </plugins> </build>
当项目中的多个模块有同样的插件配置时,应当将配置移到父POM的pluginManagement元素中
统一项目的插件版本,避免潜在的插件不一致或者不稳定问题,也更易于维护
聚合与继承
概述
聚合主要是为了方便快速构建项目
继承主要是为了消除重复配置
不同
对于聚合来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在
对于继承关系的父POM来说,它不知道有哪些子模块继承于它,但那些子模块都必须知道自己的父POM是什么
共同
(1)聚合POM与继承关系的父POM的packaging都必须是pom;
(2)聚合模块与继承关系中的父模块除了POM之外都没有实际的内容
合并聚合和继承功能后的account-parent的POM配置
<?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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Maven Account-Parent Project</name> <modules> <module>account-email</module> <module>account-persist</module> <module>account-captcha</module> <module>account-service</module> <module>account-web</module> </modules> <!-- Maven属性 --> <properties> <project.build.sourceEncoding> UTF-8 </project.build.sourceEncoding> <project.reporting.outputEncoding> UTF-8 </project.reporting.outputEncoding> <project.version>1.0.0-SNAPSHOT</project.version> <springframework.version>2.5.6</springframework.version> <junit.version>4.10</junit.version> </properties> <!-- 依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> <build> <!-- 插件 --> <pluginManagement> <plugins> <!-- 支持java 5 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> <!-- 使用UTF-8编码处理资源文件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.5</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
相应子模块的POM配置也做微小修改,本来父模块与子模块位于同级目录,现在新的account-parent在上一层目录 ,这是Maven默认能识别的父模块位置,因此无须再配置relativePath,如:
。。。 <parent> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> 。。。
约定优于配置
约定优于配置,Maven核心设计理念之一
使用约定可以大量减少配置
超级POM
Maven3:%M2_HOME%/lib/maven-model-builder-x.x.x.jar中
org/apache/maven/model/pom-4.0.0.xml
Maven2:%M2_HOME%/lib/maven-x.x.x-uber.jar中
org/apache/maven/project/pom-4.0.0.xml
任何一个Maven项目都隐式地继承自超级POM
大量超级POM的配置都会被 所有Maven继承,这些配置也就成为了Maven所提倡的约定
反应堆
在一个多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一个构建结构。
对于单模块的项目,反应堆就是该模块本身,但对于多模块项目来说,反应堆就包含了各模块之间继承与依赖的关系,从而能够自动计算出合理的模块构建顺序。
反应堆的构建顺序
Maven按序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖还依赖于其他模块,则进一步先构建依赖的依赖。
类似于递归
裁减反应堆
需求:
用户会选择构建整个项目或者选择构建单个模块,但有时,用户仅需构建完整反应堆中的某些个模块,换句话说,用户需要实时地裁减反应堆
命令:
-am, --also-make:同时构建所列模块的依赖模块
-amd, -also-make-dependents:同时构建依赖于所列模块的模块
-pl, --projects <arg>:构建指定的模块,模块间用逗号分隔
-rf, -resume-from <arg>:从指定的模块回复反应堆
mvn clean install
mvn clean install -pl account-email,account-persist
mvn clean install -pl account-email -am
mvn clean install -pl account-parent -amd
同时构建依赖于所列模块的模块
mvn clean install -rf account-email
mvn clean install -pl account-parent -amd -rf account-email
对裁剪后的反应堆再次裁剪
--------------------------------------------------------------------------------------------------------------------
@author Free Coding http://ln-ydc.iteye.com/