Maven POM 文件中的中的继承(Inheritance)和 聚合(Aggregation)- IDEA 和 Eclipse 中多模块项目的 Maven 组织方法

Maven POM 文件中的继承和聚合(Inheritance & Aggregation)


之前在 IDEA 里建了一个项目专门用来学习,每个知识点都建立一个相应的模块,方便学习和查看。今天在建第二个模块的时候发现,这个模块很多的 Maven 依赖和第一个模块相同,按照我之前一个项目仅一个模块的做法,那肯定是要把依赖再复制粘贴一遍。但是在同一个项目里面,随着模块不断增加,不能每次都复制粘贴吧,而且模块都是 Maven 工程,可以利用 Maven 来对这些模块进行管理。带着这种想法,我去官网学习了下 POM 文件中的继承和聚合。

POM

POM (Project Object Model),在我们的 Maven 项目中表现出来的就是 pom.xml 文件,是对整个 Maven 项目的组织管理文件。

Super POM

我们创建 Maven 项目的时候,或是手动创建或是自动创建,都会有一个固定的文件目录结构,那为什么必须是按照这个结构来呢?添加依赖后,项目自动会从 Maven 中央仓库下载相关的 jar 包,它是在什么地方定义这个仓库地址的?这些问题的答案就是 Super POM。我们在项目中创建的 POM 文件都是继承自这个 Super POM,它是 Maven 项目的默认 POM。下面是 Maven 2.1.x 版本的 Super POM,能看到其中定义了很多默认的配置。

<project>
  <modelVersion>4.0.0</modelVersion>
  <name>Maven Default Project</name>

  <repositories>
    <repository>
      <id>central</id>
      <name>Maven Repository Switchboard</name>
      <layout>default</layout>
      <url>http://repo1.maven.org/maven2</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

  <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>

  <build>
    <directory>${project.basedir}/target</directory>
    <outputDirectory>${project.build.directory}/classes</outputDirectory>
    <finalName>${project.artifactId}-${project.version}</finalName>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <!-- TODO: MNG-3731 maven-plugin-tools-api < 2.4.4 expect this to be relative... -->
    <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
   <pluginManagement>
       <plugins>
         <plugin>
           <artifactId>maven-antrun-plugin</artifactId>
           <version>1.3</version>
         </plugin>       
         <plugin>
           <artifactId>maven-assembly-plugin</artifactId>
           <version>2.2-beta-2</version>
         </plugin>         
         <plugin>
           <artifactId>maven-clean-plugin</artifactId>
           <version>2.2</version>
         </plugin>
         <plugin>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>2.0.2</version>
         </plugin>
         <plugin>
           <artifactId>maven-dependency-plugin</artifactId>
           <version>2.0</version>
         </plugin>
         <plugin>
           <artifactId>maven-deploy-plugin</artifactId>
           <version>2.4</version>
         </plugin>
         <plugin>
           <artifactId>maven-ear-plugin</artifactId>
           <version>2.3.1</version>
         </plugin>
         <plugin>
           <artifactId>maven-ejb-plugin</artifactId>
           <version>2.1</version>
         </plugin>
         <plugin>
           <artifactId>maven-install-plugin</artifactId>
           <version>2.2</version>
         </plugin>
         <plugin>
           <artifactId>maven-jar-plugin</artifactId>
           <version>2.2</version>
         </plugin>
         <plugin>
           <artifactId>maven-javadoc-plugin</artifactId>
           <version>2.5</version>
         </plugin>
         <plugin>
           <artifactId>maven-plugin-plugin</artifactId>
           <version>2.4.3</version>
         </plugin>
         <plugin>
           <artifactId>maven-rar-plugin</artifactId>
           <version>2.2</version>
         </plugin>        
         <plugin>                
           <artifactId>maven-release-plugin</artifactId>
           <version>2.0-beta-8</version>
         </plugin>
         <plugin>                
           <artifactId>maven-resources-plugin</artifactId>
           <version>2.3</version>
         </plugin>
         <plugin>
           <artifactId>maven-site-plugin</artifactId>
           <version>2.0-beta-7</version>
         </plugin>
         <plugin>
           <artifactId>maven-source-plugin</artifactId>
           <version>2.0.4</version>
         </plugin>         
         <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.4.3</version>
         </plugin>
         <plugin>
           <artifactId>maven-war-plugin</artifactId>
           <version>2.1-alpha-2</version>
         </plugin>
       </plugins>
     </pluginManagement>
  </build>

  <reporting>
    <outputDirectory>${project.build.directory}/site</outputDirectory>
  </reporting>
  <profiles>
    <profile>
      <id>release-profile</id>

      <activation>
        <property>
          <name>performRelease</name>
          <value>true</value>
        </property>
      </activation>

      <build>
        <plugins>
          <plugin>
            <inherited>true</inherited>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-sources</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-javadoc-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-javadocs</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-deploy-plugin</artifactId>
            <configuration>
              <updateReleaseInfo>true</updateReleaseInfo>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>

</project>

Minimal POM

既然 Super POM 中已经有很多默认的 Maven 配置,那么对于我们自己创建的 POM 文件,如果不需要添加额外的功能,只需要定义最小配置即可。参考如下

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

即项目的基本信息,其中<modelVersion>应该被设为4.0.0<groupId><artifactId><version>提供了项目组织id、项目id以及项目版本号。

项目继承

Super POM 就是一个典型的 POM 继承的例子。我们也可以自己定义模块之间的继承方式,从父模块的 POM 配置中继承配置信息,子模块就不再需要进行重复的配置。

这里有两个模块(或者称为构件:artifact),my-app 和 my-module 模块。其中 my-module 模块继承自 my-app 模块,即 my-app 模块是 my-module 模块的父模块。根据两个模块之间不同的组织结构,有两种不同的配置方法。

对于 IDEA 来说,可以建立一个 Maven 工程,再在其中添加 Maven 模块,配置模块继承工程的 POM ,这样的话项目目录结构就是 Example 1。也可以建立一个空的工程,在其中添加多个 Maven 模块,配置模块之间的 POM 关系实现继承,这样项目目录结构就是 Example 2。对于 Eclipse 来说,就只能建立 Example 2 的目录结构了。

  • 第一种结构 Example 1:
my-app
 |-- my-module
 |   `-- pom.xml
 `-- pom.xml

my-module 模块是 my-app 模块的一部分,处于 my-app 模块的根目录下,与 my-app 的 pom.xml 文件平级。这里可以通过在子模块 my-module 里的 POM 中添加一下内容实现继承。

<project>
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-module</artifactId>
  <version>1</version>
</project>

这里在<project>中指定了<parent>元素,其中声明了父模块的组织信息。如果希望子模块继承父模块的<groupId><version>,删掉子模块中的<groupId><version>即可。

  • 第二种结构 Example 2:
.
 |-- my-module
 |   `-- pom.xml
 `-- my-app
     `-- pom.xml

这种结构中,my-module 和 my-app 模块在相同的目录下,两者之间处于平级关系。Eclipse 中各个项目之间就是这种目录层级关系。这种情况下我们可以添加一个<relativePath>元素到子模块的<parent>中,来实现继承关系,而其值就是 POM 文件的相对路径。

<project>
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
    <relativePath>../my-app/pom.xml</relativePath>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>my-module</artifactId>
</project>

这里 my-module 继承了 my-app 的 <groupId><version>,这样也就实现了继承。

POM 中多模块项目的聚合和继承针对的都是以上两种目录结构的项目。

项目聚合

聚合与继承相似,不同的是继承方式配置的是子模块的 POM,而聚合方式配置的是父模块的 POM(即 pom.xml 文件)。操作步骤如下

1. 将父模块 POMs 的 packaging 值设为 "pom"
2. 在父模块 POM 中指定子模块的路径
  • 第一种结构 Example 1:
my-app
 |-- my-module
 |   `-- pom.xml
 `-- pom.xml

针对这种结构,我们在 my-app 模块(父模块)的 POM 文件中添加<modules>元素进行配置

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>

  <modules>
    <module>my-module</module>
  </modules>
</project>

注意,在聚合方式中,父模块的打包方式<packaging>发生了改变,这里是pom而不再是war<module>元素中使用的是子模块相对于父模块的路径。

通过这种配置方式,在 my-app 上执行的 Maven 命令也会在 my-module 上运行。

  • 第二种结构 Example 2:
.
 |-- my-module
 |   `-- pom.xml
 `-- my-app
     `-- pom.xml

参考第一种结构,我们使用了<module>元素中模块之间的相对路径,那么对于第二种结构,我们修改下相对路径即可。

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>

  <modules>
    <module>../my-module</module>
  </modules>
</project>

项目继承和聚合的组合使用

首先说一下继承和聚合使用的场景:
继承:有多个 Maven 项目,它们具有相似的 POM 配置信息。我们可以重构所有项目,将相似的配置信息抽取出来,放到新创建的父项目里,然后让所有 Maven 项目继承父项目,这样就是继承的用法。
聚合:有一组项目,它们同时编译或执行,那我们可以创建一个父项目,将这组项目声明为父项目的多个子模块,这样的话,我们就只用编译或运行父项目即可。这是聚合的使用场景。

我们在实际开发过程中,会组合使用继承和聚合。比如说,我们需要开发一个项目,这个项目由多个模块组成,这些模块用到的 POM 配置信息相似,而且模块之间会有依赖关系,某个模块依赖于另外一个模块才能正常运行。这样情况我们就可以组合使用继承和聚合,能大大的减少项目的配置和管理成本。

使用继承和聚合,需要遵循3个规定:

1. 在每个子 POM 中指定它们的父 POM
2. 将父 POMs 打包方式设为 "pom"
3. 在父 POM 中指定子模块的路径

我们使用第二种结构作为示例。第一种结构与其相似,去掉 <relativePath>即可。

my-app 项目中:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
  <packaging>pom</packaging>

  <modules>
    <module>../my-module1</module>
    <module>../my-module2</module>
    <module>../my-module3</module>
  </modules>
</project>

my-module1 项目中:

<project>
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1</version>
    <relativePath>../my-app/pom.xml</relativePath>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>my-module1</artifactId>
</project>

my-module2、my-module3 项目配置与 my-module1 相似,不列出。

实际开发过程中,多模块的管理方式可以是继承和聚合的组合使用。我在之前建立的用于学习的项目,可以仅仅使用继承。其中为了将 POM 配置信息独立出来,可以新建一个模块,这个模块只有 POM 配置信息,其他的模块依赖这个模块,即可实现 Maven 依赖的复用。

项目中的变量

Maven 允许在 POM 中使用自定义的和预设的变量。自定义变量的方式:

<properties>
    <spring.version>4.3.6.RELEASE</spring.version>
</properties>

使用方式:

<version>${spring.version}</version>

实战:多模块项目 POM 示例

项目模块组织关系:
项目有一个核心模块,提供了项目其他模块运行时的各种组件。依赖于这个核心模块,项目提供了 浏览器 和 App 两种基础服务后台。在 浏览器 后台上,构建一个 Demo 项目,用于展示项目开发成果。

使用 Maven 管理:
首先构建一个父模块,所有的模块都继承这个父模块

parent-module:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>big-project</groupId>
    <artifactId>parent-module</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
         <p.version>1.0-SNAPSHOT</p.version>
    </properties>
    ...

    <modules>
        <module>../core-module</module>
        <module>../browser-module</module>
        <module>../app-module</module>
        <module>../demo-module</module>
    </modules>
</project>

建好后,和其他模块的目录结构关系如下:

.
 |-- parent-module
    `-- pom.xml
 |-- core-module
     `-- pom.xml
 |-- app-module
    `-- pom.xml
 |-- browser-module
     `-- pom.xml
 |-- demo-module
     `-- pom.xml

其他模块 POM 配置如下

core-module:

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>core-module</artifactId>
    <parent>
        <groupId>big-project</groupId>
        <artifactId>parent-module</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../parent-module</relativePath>
    </parent>

    ...

</project>

app-module:

<project>
    <artifactId>app-module</artifactId>
    <parent>
        <groupId>big-project</groupId>
        <artifactId>parent-module</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../parent-module</relativePath>
    </parent>

    <dependencies>
        <dependency>
            <groupId>big-project</groupId>
            <artifactId>core-module</artifactId>
            <version>${p.version}</version>
        </dependency>

        ...

    </dependencies>
</project>

browser-module:

<project>
    <artifactId>browser-module</artifactId>
    <parent>
        <groupId>big-project</groupId>
        <artifactId>parent-module</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../parent-module</relativePath>
    </parent>

    <dependencies>
        <dependency>
            <groupId>big-project</groupId>
            <artifactId>core-module</artifactId>
            <version>${p.version}</version>
        </dependency>

        ...

    </dependencies>
</project>

demo-module:

<project>
    <artifactId>demo-module</artifactId>
    <parent>
        <groupId>big-project</groupId>
        <artifactId>parent-module</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../parent-module</relativePath>
    </parent>

    <dependencies>
        <dependency>
            <groupId>big-project</groupId>
            <artifactId>browser-module</artifactId>
            <version>${p.version}</version>
        </dependency>

        ...

    </dependencies>
</project>

在 parent-module 中,配置了多个 <module>元素,其他的模块也相应地配置了 <parent>信息,这样在编译或执行 parent-module 时,其他模块也都进行了编译或运行。
app-module 和 browser-module 均依赖于 core-module,均使用了 <dependency>来完成这个依赖。
demo-module 依赖于 browser-module,使用<dependency>来完成这个依赖。

参考资料:

官网链接:https://maven.apache.org/guides/introduction/introduction-to-the-pom.html
慕课网实战:https://coding.imooc.com/learn/list/134.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值