Maven的聚合特性能够把项目的各个模块聚合在一起构建; Maven的继承特性能够帮助抽取各模块相同的依赖和插件等配置,在简化POM的同时,还能够促进各个模块配置的一致性。
account-persist模块还需要一个SpringFramework的配置文件,位于src/main/resources目录:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="propertyConfigure" class="org.springframework.beans.factory.config.PropertyPlaceholderconfigure"> <property name="location" value="classpath:account-service.properties"/> </bean> <bean id="accountPersistService" class="com.park.mvnDemo.account.persist.AccountPersistServiceImpl"> <property name="file" value="${persist.file}"/> </bean> <beans>我们想要一次构建两个项目,而不是到两个模块的目录下分别执行mvn命令 -- Maven的聚合就是为该需求服务的。
为了能够使用一条命令就能构建account-email和account-persist两个模块,需要创建一个额外的名为account-aggregator的模块,然后通过该模块构建整个项目的所有模块。
account-aggregator也有它自己的POM文件,内容如下:
<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.park.mvnDemo.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>在上面的xml文件中,packaging的方式为pom。对于聚合模块来说,其打包方式必须为pom,否则无法构建!
modules: 这里是实现聚合的最核心的配置,可以声明任意数量的module元素来实现元素的聚合;其中,每个module的值为当前POM的相对路径;如:当前POM位于D:\m2\code\account-aggregator\pom.xml, 另有一个项目A位于D:\m2\code\account-aggregator\account-email/,一个项目B位于D:\m2\code\-aggregatoraccount\account-persist/,与上面的module值相对应。为了方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在。在我们已经聚合的项目中,有很多重复的配置,有相同的groupId和version,有相同的spring-core, spring-beans, spring-context和juit依赖,造成大量的浪费也会引发问题,所以如何使用继承机制来统一配置这些重复的信息,做到”一处声明,处处使用“呢?
思路:创建POM的父子结构,在父POM中声明一些配置供子POM继承、
在account-aggregator目录下创建POM的父目录account-parent,内部的pom文件内容为:
<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.park.mvnDemo.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0</version> <packaging>dom<packaging> <name>Account Parent</name> </project>作为父模块的POM,其打包类型也是只能为pom。
由于父模块只是为了帮助消除配置的重复,因此她本身不包含除POM外的项目文件,也就不需要src/main/java之类的文件了。
(子模块account-persist的pom配置与之类似,不再赘述)
dependencies可以使得子模块继承父模块依赖的插件,同时dependencyManagement元素又能保证子模块使用的灵活性。在dependencyManagement元素下的声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。 示例:
(一) 在account-parent中配置dependencyManagement元素
<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.park.mvnDemo.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0_SNAPSHOT</version> <packaging>dom</packaging> <name>Account Parent</name> <properties> <springframework.version>2.5.6</springframework.version> <junit.version>4.7</junit.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifact> <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> </depedencyManagement> </project>(二) 继承了dependencyManagement的account-email POM
<properties> <javax.mail.version>1.4.1</javax.mail.version> <greenmail.version>1.3.1b</greenmail.version> </properties> <dependencies> <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>如果不声明某个依赖,那这个依赖就不会被引入。
Maven提供了pluginManagement元素来管理插件。在该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件相匹配时,pluginManagement的配置才会影响实际的插件行为。
(一) 在父POM中配置pluginManagement
<build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.1.1</version> <executions> <execution> <id>attach-sources</id> <phase>verify<phase> <goals> <goal>jar-no-fork</goal> </goals> </exeuction> </executions> </plugin> </plugins> </pluginManagement> </build>(二) 在子模块中配置相应的插件
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> </plugin> </plugins> </build>在实际的项目中,一个POM可以既是聚合POM,又是父POM。 如果父模块位于子模块们的上级,则不需要再额外配置relativePath,因为Maven默认能识别父模块的位置。
在一个多模块的Maven项目中,反应堆是指所有模块组成的一个构件结构。包含了各模块之间继承于依赖的关系,从而能够自动计算出合理的模块构件顺序。
Maven实际的构建顺序是:Maven按序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖模块还依赖于其他模块,则进一步先构建其他模块。
模块间的依赖关系会将反应堆构成一个有向无环图(DAG: Directed Acyclic Graph),各个模块是该图的节点,依赖关系构成了有向边。
当用户并不想构建整个项目的时候,就需要实时地裁剪反应堆。 Maven提供很多命令行支持裁剪反应堆:
-am, --alsomake: 同时构建所列模块的依赖模块模块;-amd, --also-make-dependents: 同时构建依赖于所列模块的模块;-pl, --projects<arg>: 构建指定模块,模块间用逗号分割;-rf, -resume-from<arg>: 从指定的模块回复反应堆。
\$ mvn clean install \\Output: Account Aggregator, Account Parent, Account Email, Account Persist \$ mvn clean install -pl account-email,account-persist \\Output: Account Email, Account Persist \$ mvn clean install -pl account-email -am \\Output: Account Parent, Account Email \$ mvn clean install -pl account-parent -amd \\Output: Account Parent, Account Email, Account Persist \$ mvn clean install -rf account-email \\Output: Account Email, Account Persist \$ mvn clean install -pl account-parent -amd -rf account-email \\Output: Account Email, Account Persist