Maven POM元素继承

        为了减少重复代码的编写,我们需要创建POM的父子结构,然后在POM中申明一些配置供子POM继承,以实现“一处申明,多处使用的”目的。以之前的模块中的结构为基础,在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/maven-v4_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>Account Parent</name>     
</project> 

        需要注意的是它的packaging的值必须为pom,这一点与模块聚合一样,作为负模块的POM,其打包类型也必须为pom。由于父模块只是为了消除配置的重复,因此也就不需要src/main/java等目录了。

        有了父模块就让其他子模块来继承它,修改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/maven-v4_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>Account Email</name> 

        <dependencies>
                ......
        </dependencies>

        <build>
                <plugins>
                    ......
                </plugins>
        </build>

</project>

        parent下的子元素groupId、artifactId和version指定了父模块的坐标,这三个元素是必须的。元素relativePath表示父模块POM的相对路径。当项目构建时,Maven会首先根据relativePath检查父POM,如果找不到,再从本地仓库查找。relativePath的默认值是../pom.xml,也就是说,Maven默认父POM在上一层目录下。

        这个更新过的POM文件并没为account-email设置artifactId和version,实际上这个子模块隐式的从父模块继承了这两个元素。如果子模块中遇到与父模块中不一样的artifactId、version这个时候应该在子模块显示声明。

        最后还要把父模块加入到聚合模块account-aggregator中如下:

<?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-parent</module>    
             <module>account-email</module>  
             <module>account-persist</module>  
        <modules>     
</project>    

此时的目录结构是account-parent、account-email、account-persist在同一级目录,account-aggregator为三个目录的父目录。

可继承的POM元素如下:

  • groupId:项目组ID,项目坐标的核心元素
  • version:项目版本,项目坐标的核心因素
  • description:项目的描述信息
  • organization:项目的组织信息
  • inceptionYear:项目的创始年份
  • url:项目的URL地址
  • developers:项目的开发者信息
  • contributors:项目的贡献者信息
  • distributionManagement:项目的部署配置
  • issueManagement:项目的缺陷跟踪系统信息
  • ciManagement:项目的持续集成系统信息
  • scm:项目的版本控制系统西溪
  • malilingLists:项目的邮件列表信息
  • properties:自定义的Maven属性
  • dependencies:项目的依赖配置
  • dependencyManagement:项目的依赖管理配置
  • repositories:项目的仓库配置
  • build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等
  • reporting:包括项目的报告输出目录配置、报告插件配置等

  依赖管理

  因为dependencies元素也是可继承的,因此我们可以把公共的jar放到父模块account-parent中,比如org.springframework:spring-core:2.5.6在account-email,account-persist模块中都会用到,因此可以将这个依赖配置放到父模块中,两个子模块就能移除这些依赖从而简化配置。

  上述做法是可行的,但是存在问题。因为所有的子模块都会继承以上父模块的依赖项,不管子模块是否真的需要此依赖项。这样相当于子模块失去了这些依赖项继承的选择权。假设后面加了模块acount-util与springframework没有半毛钱关系,让他依赖spring的依赖项显然是不合理的。

  为此Maven提供了dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖的使用灵活性。在dependencyManagement元素下的依赖声明不会引入到实际的依赖,不过它能约束dependencies下的依赖使用。

<?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-parent</artifactId>  
        <version>1.0.0-SNAPSHOT</version>  
        <packaging>pom</packaging>  
        <name>Account Parent</name> 
        <properties>
                <springframework.version>2.5.6</springframework.version>
        </properties>

        <dependencyManagement>
                <dependencies>
                       <dependency>
                            <groupId>org.springframework </groupId>
                            <artifactId>spring-core</artifactId>
                            <version>${springframework.version}</version>
                       </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/maven-v4_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>Account Email</name> 

        <dependencies>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                </dependency>
                
                ......
                
        </dependencies>

        <build>
                <plugins>
                    ......
                </plugins>
        </build>

</project>

  上面的配置较原来简单了一些,只配置了groupId和artifactId,省去了version,如果父模块配置了依赖的scope也是可以省略的。这些信息可以省略是因为account-email继承了account-parent中的dependencyManagement配置,完整的依赖声明已经包含在父POM中了,子模块只需简单的配置groupId和artifactId。

  使用这种以来管理机制虽然不能减少太多的配置项,但是经过别人实践后强烈推荐的方法。如果子模块不声明依赖的使用,即使该依赖已经在父POM文件dependencyManagement中声明了,也不会产生任何实际的效果。

 

插架管理

  Maven提供了dependencyManagement元素帮助管理依赖,类似的,Maven也提供了pluginManagement元素帮助管理插件。当子模块POM中配置了plugin,并且groupId和artifactId与pluginManagement中的配置的插件匹配时,pluginManagement的配置才会真正影响实际插件的行为。

  父POM文件:

  <build>
  <pluginManagement>
  <plugins>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-source-plugin</artifactId>
    <version>2.1.2</version>
    <executions>
      <execution>
        <id>attach-sources</id>
        <phase>verify</phase>
        <goals>
          <goal>jar-no-fork</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
  </plugins>
  </pluginManagement>
  </build>

  在子模块继承pluginManagement后的插件配置

  <build>
      <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
          </plugin>
        </plugins>
  </build>

 

聚合与继承的关系

区别 :

1.对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。

2.对于继承关系的父 POM来说,它不知道有哪些子模块继承与它,但那些子模块都必须知道自己的父 POM是什么。

 

共同点 :

1.聚合 POM与继承关系中的父POM的 packaging都是pom

2.聚合模块与继承关系中的父模块除了 POM之外都没有实际的内容。

Maven聚合关系与继承关系的比较
 
现有的实际项目往往是一个POM既是聚合POM又是父POM,这么做主要是为了方便。
 
反应堆:
  在一个多模块的Maven项目中,反应堆是指所有模块组成的一个构建结构。反应堆包含了各个模块之间的继承与依赖关系,从而能够自动计算出合理的模块构建顺序。
<modules>  
  <module>account-email</module>  
  <module>account-persist</module>  
  <module>account-parent</module>  
</modules> 

 

  实际的构建顺序为: ~parent, ~email, ~persist。

  可见构建的顺序并不是声明的顺序。

  该例中account-email和account-persist都依赖于account-parent,那么account-parent就必须在另外两个模块之前构建。实际的构建顺序是这样的:Maven按声明顺序读取POM,如果POM没有依赖模块,那么久构建该模块,否则就先构建其依赖的模块。,如果依赖的模块还依赖于其他模块,则进一步构建依赖的依赖。

  模块之间的关系将反应堆构成一个有向非循环图,各个模块是该图的节点,依赖关系构成又向边,这个图不允许出现循环,因此当出现A依赖于B,B又依赖于A的情况时Maven就会报错。

 

裁剪反应堆:

mvn -h可以看到这样一些选项:

Options:  
 -am,--also-make                        If project list is specified, also  
                                        build projects required by the  
                                        list  
 -amd,--also-make-dependents            If project list is specified, also  
 -pl,--projects <arg>                   Build specified reactor projects  
                                        instead of all projects. A project  
                                        can be specified by  
                                        [groupId]:artifactId or by its  
                                        relative path.  
 -rf,--resume-from <arg>                Resume reactor from specified  
                                        project  

  有的时候因为工程巨大,模块很多,只想构建指定的模块,这时候就会用到反应堆裁剪。

-pl 构建指定模块
mvn clean install -pl accouont-email,account-persist后会得到如下几个模块:~email和~persist。

 

-am 选项可以同时构建所列模块的依赖模块。
当执行mvn clean install -pl account-email -am 后会得到如下几个模块:~parent和~email。

 

-amd 选项可以同时构建依赖于所列模块的模块。
mvn clean install -pl account-parent -amd 后会得到如下模块:~parent、~email和~persist。

 

-rf 选项可以在完整的反应堆构建顺序基础上,指定从哪个模块开始构建。
mvn clean install -rf account-email ,由于~email位于第三,它之后有~persist,因此会得到如下输出模块:~email和~persist。

 

在-pl -am或者-pl -amd的基础上,还能应用-rf参数,以对裁剪后的反应堆再次裁剪。
mvn clean install -pl account-parent -amd -rf account-email
其中,-pl 和 -amd参数会得到:~parent, ~email和~persist模块,在此基础上 -rf参数制定从~email开始构建,于是会得到:~email和~persist。

posted @ 2016-10-24 17:24  海阔天空990  阅读(23928)  评论(2编辑  收藏  举报