代码的第一行是xml头,指定了该xml文档的版本和编码方式
project是所有pom.xml的根元素,还声明了一些POM相关的命名空间及xsd元素。
modelVersion指定了当前POM模型的版本。对于Maven2和Maven3来说,他只能是4.0.0
groupId,artifactId和version这三个元素定义了一个项目基本的坐标
groupId定义了项目属于哪个组,artifactId定义了当前Maven项目在组中唯一的ID
name声明了一个对于用来更为友好的项目名称
<?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/PO,/4.0.0
http://maen.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maven_in_action</groupId>
<artifactId>hello-world</artifactId>
<version>1.0-SANPSHOT</version>
<name>Maven Hello World Project</name>
</project>
compiler插件默认只支持编译java1.3,因为build在执行complier:testCompile时会导致任务失败。因为在POM中可以对compiler插件进行全局配置
<?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/PO,/4.0.0
http://maen.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maven_in_action</groupId>
<artifactId>hello-world</artifactId>
<version>1.0-SANPSHOT</version>
<name>Maven Hello World Project</name> <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
要生成可执行的jar文件,需要借助maven-shade-plugin。项目在打包时会将信息放到MANIFEST中。
<?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/PO,/4.0.0
http://maen.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maven_in_action</groupId>
<artifactId>hello-world</artifactId>
<version>1.0-SANPSHOT</version>
<name>Maven Hello World Project</name> <build>
<plugins> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin> <plugin>
<grougId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformesr>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.maven_in_action.HelloWorld<mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
根元素project下的dependencies可以包含一个或者多个dependency元素,以声明一个或多个项目的依赖。
每个依赖可以包含以下元素:
groupId,artifactId,version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的
type:依赖的类型,对应于项目坐标定义的packaging。默认为jar
scope:依赖的范围
Maven在编译项目主代码的时候需要使用一套classpath,称为编译classpath。Maven在执行测试的时候会使用另一套classpath,称为测试classpath。Maven在实际运行的时候会又使用一套classpath,称为运行classpath。依赖范围就是用来控制依赖与这三种classpath的关系的
Maven有以下几种依赖范围
compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三总classpath都有效。
test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或运行项目的使用时将无法使用此依赖。
provided:以提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。通常在运行项目时,jar包已由容器提供。
runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。
system:系统依赖范围。和provided依赖范围一致,在编译和测试时有效,在运行时无效。但使用system范围的依赖必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven库解析的,而是与本机系统绑定,会有不可移植的问题。systemPath元素还可以引用环境变量。
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
import:导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。
optional:标记以来是否可选
exclusions:用来排除传递性依赖
依赖范围不仅可以控制依赖与三种classpath的关系,还对传递性依赖产生影响。假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。Maven依赖调解的第一条原则是:路径最近者优先;Maven依赖调解的第二条原则:第一声明优先。在依赖路径长度相等的前提下,在POM中以来声明的顺序决定了啦谁会被解析使用,顺序最靠前的那个依赖优先。
可选依赖
假设A依赖于B,B依赖于X和Y,B对于X和Y的依赖是可选依赖。根据依赖性传递的定义,依赖将不会得以传递,换句话说,X,Y将不会对A产生影响。X,Y只在B项目总可用,在A中失效,若在A中想使用B中可选依赖,需要在A的dependency中重新声明。在理想情况下是不应该使用可选依赖的,违反了单一职责。
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.10</version>
<optional>true</optional>
</dependency> <dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.4-701.jdbc3</version>
<optional>true</optional>
</dependency>
</dependencies>
排除依赖
代码中使用exclusions元素声明排除依赖,exclusions可以包含一个或多个exclusion子元素,因此可以排除一个或多个传递性依赖。声明exclusion的时候只需要groupId和artifactId,而不需要version元素,因为groupId和artifactId能唯一定位依赖图中的某个依赖。
<dependencies>
<dependency>
<groupId>com.maven_in_action</groupId>
<artifactId>project-b</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>com.maven_in_action</groupId>
<artifactId>project-c</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.maven_in_action</groupId>
<artifactId>project-c</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
使用properties元素定义Maven的属性。有了这个属性定义后,Maven运行的时候会将POM中的所有${xx.version}替换成定义的值。也就是说可以使用美元符号和大括号环绕的方式引用Maven属性。
<?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/PO,/4.0.0
http://maen.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maven_in_action</groupId>
<artifactId>hello-world</artifactId>
<version>1.0-SANPSHOT</version>
<name>Maven Hello World Project</name> <properties>
<springframework.version>2.5.6</springframework.version>
</properties> <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>
</dependencies>
</project>
Maven可以将项目生成的构建部署到远程仓库中。distributionManagement包含了repository和snapshotRepository子元素,前者表示发布版本构件的仓库,后者表示快照版本的仓库。id为该远程仓库的唯一标识,name为方便人阅读,url表示该仓库的地址。快照只应该在组织内部的项目或模块间依赖使用,组织对于这些快照版本的依赖具有完全的理解及控制权。项目不应该依赖于任何组织外部的快照版本依赖。
<project> <distributionManagement>
<repository>
<id>proj-releases</id>
<name>Proj Release Repository</name>
<url>http://192.168.1.100/content/repositories/proj-releases</url>
</repository>
<snapshotRepository>
<id>proj-snapshots</id>
<url>http://192.168.1.100/content/repositories/proj-snapshots</url>
</snapshotRepository>
</distributionManagement> </project>
Maven依赖解析机制:
当依赖的范围是system的时候,Maven直接从本地文件系统解析构件
根据依赖坐标计算仓库路径后,尝试直接从本地仓库寻找构件,若发现相应构件,则解析成功
在本地仓库不存在相应构件的情况下,若依赖的版本是显式的发布版本构件,则遍历所有的远程仓库看,发现后,下载并解析使用
如果依赖的版本是release或latest,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/maven-metadata.xml,将其与本地仓库的对应元数据合并后,计算出release或latest真实的值,然后基于这个真实的值检查本地和远程仓库
如果依赖的版本是SNAPSHOT,则基于更新策略读取所有远程仓库的元数据groupId/artifactId/version/maven-metadata.xml。将其与本地仓库的对应元数据合并后,得到最新快照版本的值,然后基于该值检查本地仓库,或者从远程库下载
如果最后解析得到的构件版本是时间戳格式的快照,则复制器时间戳格式的位置至非时间戳格式,并使用该非时间戳格式的构件。
当依赖的版本不明晰的时候,如RELEASE(最新发布版本),LASTEST(最新发布版本包含快照)和SNAPSHOT,Maven就需要基于更新基于远程仓库的更新策略来检查更新。当Maven检查完更新策略,并决定检查以来更新的时候,就需要检查仓库元数据maven-metadata.xml。RELEASE和LATEST是基于groupId/artifactId/maven-metadata.xml计算出来的。Maven通过合并多个远程仓库及本地仓库的元数据,可以计算出基于所有仓库的latest和release,然后解析具体的构件。在依赖声明中使用LASTEST和RELEASE是不推荐的。
<?xml version="1.0" encoding="UTF-8" ?>
<metadata>
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus</artifactId>
<versioning>
<lastest>1.4.2-SNAPSHOT</lastest>
<release>1.4.0</release>
<versions>
<version>1.3.5</version>
<version>1.3.6</version>
<version>1.4.0-SNAPSHOT</version>
<version>1.4.0</version>
<version>1.4.0.1-SNAPSHOT</version>
<version>1.4..2-SNAPSHOT</version>
</versions>
<lastUpdated>20091214221557</lastUpdated>
</versioning>
</metadata>
当依赖的版本设为快照版本的时候,Maven也需要检查更新,Maven会检查仓库元数据groupId/artifactId/version/maven-metadata.xml。
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus</artifactId>
<version>1.4.2-SNAPSHOT</version>
<versioning>
<snapshot>
<timestamp>20091214.221414</timestamp>
<buildNumber>13</buildNumber>
</snapshot>
<lastUpdated>20091214221558</lastUpdated>
</versioning>
</metadata>
snapshot元素包含了timestamp和buildNumber,分别代表了这一快照的时间戳和构建号,基于这两个元素可以得到该仓库中此快照的最新构件版本。通过合并所有远程仓库和本地仓库元数据,Maven就能知道所有仓库中该构件的最新快照。
用户可以自己选择将某个插件目标绑定到生命周期的某个阶段
<build>
<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>
</execution>
</executions>
</plugin>
</plugins>
</build>
executions下每个execution子元素可以用来配置执行一个任务。上述xml中配置了一个id为attach-sources的任务,通过phase配置,将其绑定到verify生命周期阶段上,再通过goals配置指定要执行的插件目标。运行mvn verify,maven-source-plugin:jar-no-fork会得以执行,它会创建一个以-sources.jar结尾的源码文件包。
在POM中为插件任务配置参数
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>ant-validate</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to validate phase</echo>
</tasks>
</configuration>
</execution>
<execution>
<id>ant-verify</id>
<phase>verify</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to verify phase</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
插件全局配置中的configuration元素位于plugin元素下面。特定任务配置是configuration元素位于execution下面。
Maven会区别对待依赖的远程仓库与插件的远程仓库。当Maven需要的依赖在本地仓库不存在时,会去远程仓库中寻找,但当Maven的插件在本地仓库不存在时,它不会去远程仓库中查找。插件的远程仓库使用pluginRepositories和pluginRepository配置
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Repository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default<layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
在POM中配置插件的时候,如果该插件是Maven的官方插件,就可以省略groupId配置。
为了简化插件的配置和使用,在用户没有提供插件版本的情况系啊,Maven会中解析插件版本。首先,Maven在超级POM中为所有核心插件设定了版本,超级POM是所有Maven项目的父POM,所有项目都继承这个超级POM的配置,因此,即使永不不加任何配置,Maven使用核心插件的时候,他们的版本就已经确定了。如果用户使用某个插件时没有设定版本,而这个插件又不属于核心插件的范畴,Maven会检查所有仓库中的可用版本,然后做出选择。Maven会遍历本地仓库和所有远程插件仓库,将该路径下的仓库元数据归并后,计算latest和release的值。Maven2中,插件的版本会被解析至lastest。Maven3调整了解析机制,当插件没有声明版本的时候,不再解析至latest,而是使用release。
插件前缀与groupId:artifactId是一一对应的,这种匹配关系存储在仓库元数据中。这里的仓库元数据不是groupId/artifactId/maven-metadata.xml,而是groupId/maven-metadata.xml。当Maven解析到前缀后,首先会基于默认的groupId归并所有插件仓库的元数据org/apache/maven/plugins/maven-metadata.xml;其次检查归并后的元素,找到对应的artifactId为maven-dependency-plugin;然后结合当前元数据的groupId.org.apache.maven.plugins;最后获取version,这是就得到了完整的插件坐标。若org/apache/maven/plugins/maven-metadata.xml没有记录该插件前缀,则接着检查其他groupId下的元数据,如org/codehaus/mojo/maven-metadata.xml以及用户自定义的插件。若所有元数据都不包含该前缀,则报错。
Maven聚合
用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合。这里每个module的值都是一个当前POM的相对目录。为了方便构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在。Maven会首先解析聚合模块的POM,分析要构建的模块并计算出一个反应堆构建顺序,然后根据这个顺序依次构建各个模块。反应堆是所有模块组成的一个构建结构。
Maven继承
<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.maven_in_action</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Parent</name>
</project>
<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> <parent>
<groupId>com.maven_in_action</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent> <artifactId>child-a</artifactId>
<name>Child-A</name>
</project>
parent下的子元素groupId,artifactId和version指定了父模块的坐标,这三个元素是必须的。relativePath表示父模块POM的相对路径。当构建项目时,Maven会首先根据relativePath检查父POM,若找不到,再从本地仓库查找。
POM以下元素可以继承:
groupId:项目组ID,项目坐标的核心元素
version:项目本版本,项目坐标核心元素
description:项目的描述信息
organization:项目的组织信息
inceptionYear:项目的创世年份
url:项目的URL地址
developers:项目的开发者信息
contributors:项目的贡献者信息
distributionManagement:项目的部署配置
issueManagement:项目的缺陷跟踪信息
ciManagement:项目的持续集成系统信息
scm:项目的版本控制系统信息
mailingLists:项目的邮件列表信息
properties:自定义的Maven属性
dependencies:项目的依赖配置
dependencyManagement:项目的依赖管理配置
repositories:项目仓库的配置
build:包括项目的源码目录配置,输出目录配置,插件配置,插件配置管理等。
reporting:项目的报告输出目录配置、报告插件配置等。
Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。
child-a继承了parent中dependencyManagement配置,完整的依赖声明已经包含在父POM中,子模块只需要配置简单的groupId和artifactId就能获得对应的依赖信息,从而引入正确的依赖。这种依赖管理机制并不能减少太多的POM配置,但还是推荐采用这种方法。主要原因在于父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.maven_in_action</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>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</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>
<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> <parent>
<groupId>com.maven_in_action</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent> <artifactId>child-a</packaging>
<name>Child A</name> <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>
</dependencies>
</project>
import依赖范围只有在dependencyManagement下才有效果,使用该范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。若想在一个模块中使用另一个模块中的dependencyManagement,除了复制配置或者继承外,还可以使用import范围依赖导入之这一配置。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.maven_in_action</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Maven提供了pluginManagement元素帮助管理插件。该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。
父模块
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apahe.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>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
子模块
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
</plugins>
</build>
若子模块不需要使用父模块中的pluginManagement配置的插件,可以将其忽略。若子模块需要不同的插件配置,则可以自行配置以覆盖父模块的pluginManagement配置。
对于聚合模块来说,它知道哪些被聚合的模块,但那些被聚合的模块不知道这个模块的存在;对于继承关系的父POM来说,他不知道哪些子模块继承于它,但那些子模块都必须知道自己的父POM是什么。前者主要为了方便快速构建项目,后者主要为了消除重复配置。
对于一个多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一个构建结构。对于单模块的项目,反应堆就是该模块本身,但对于多模块项目来说,反应堆就包含了各模块之间继承和依赖的关系,从而能够自动计算出合理的模块构建顺序。
Maven的构建顺序:Maven按序读取POM,如果该POM没有依赖模块,那么就构建该POM,否则先构建其依赖模块,如果该依赖还依赖于其他模块,则进一步先构建依赖的依赖。
模块间的依赖关系会将反应堆构成一个有向非循环图,各个模块是该图的节点,依赖关系构成了有向边。这个图不允许出现循环。若A依赖B,B依赖A,Maven会报错