Maven:继承。

面向对象设计中,程序员可以建立一种类的父子结构,然后在父类中声明一些字段和方法供子类继承,这样就可以做到“一处声明,多处使用”。类似的,我们需要创建POM的父子结构,然后在父POM中声明一些配置供子POM继承,以实现“一处声明,多处使用”的目的。

我们在模块a下创建一个名为b的子目录,然后在该子目录下建立一个所有除a之外模块的父模块。为此,在该子目录创建一个pom.xml文件,内容如下。

<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.test</groupId>
    <artifactId>b</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>b</name>
</project>

该POM十分简单,他使用了与其他模块一致的groupId和version,使用的artifactId为b表示这时一个父模块。需要特别注意的是,他的packaging为pom,这一点与聚合模块一样,作为父模块的POM,其打包类型页必须为pom。

由于父模块只是为了帮助消除配置的重复,因此他本身不包含除POM之外的项目文件,也就不需要src/main/java/之类的文件夹了。

有了父模块,就需要让其他模块来继承他。首先将c的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>
    <parent>
        <groupId>com.test</groupId>
        <artifactId>b</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../b/pom.xml</relativePath>
    </parent>
    <artifactId>c</artifactId>
    <name>c</name>
    <dependencies>
        ...
    </dependencies>
    <build>
        <plugins>
            ...
        </plugins>
    </build>
</project>

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

正确设置relativePath非常重要。考虑这样一个情况,开发团队的新成员从源码库签出一个包含父子模块关系的Maven项目。由于只关心其中的某一个子模块,他就直接到该模块的目录下执行构建,这个时候,父模块是没有被安装到本地仓库的,因此如果子模块没有设置正确的relativePath,Maven将无法找到父POM,这将直接导致构建失败。如果Maven能够根据relativePath找到父POM,他就不需要再去检查本地仓库。

这个更新过的POM没有为account-email声明groupId和version,不过这并不代表c没有groupId和version。实际上,这个子模块隐式的从父模块继承了这两个元素,这也就消除了一些不必要的配置。在该例中,父子模块使用同样的groupId和version,如果遇到子模块需要使用和父模块不一样的groupId或者version的情况,那么用户完全可以在子模块中显式声明。对于artifactId元素来说,子模块应该显式声明,一方面,如果完全继承groupId、artifactId和version,会造成坐标冲突;另一方面,即使使用不同的groupId或version,同样的artifactId容易造成混淆。
最后,同样还需要把a加入到聚合模块d中,见下面。

<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.test</groupId>
    <artifactId>d</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>d</name>
    <modules>
        <module>a</module>
        <module>b</module>
        <module>c</module>
    </modules>

</project>

可继承的POM元素

在上面我们看到,groupId和version是可以被继承的,那么还有哪些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下的依赖使用。例如,可以在a中加入这样的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.test</groupId>
    <artifactId>a</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>a</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>junit<groupId>
                <artifactId>junit</artifactId>
                <version>{junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

首先将springframework和junit依赖的版本以Maven变量的形式提取了出来,不仅消除了一些重复,也使得各依赖的版本处于更加明显的位置。

这里使用dependencyManagement声明的依赖既不会给a引入依赖,也不会给他的子模块引入依赖,不过这段配置是会被继承的。现在修改b的POM如下所示。

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

上述POM中的依赖配置较原来简单了一些,所有的springframework依赖只配置了groupId和artifactId,省去了version,而junit依赖不仅省去了version,还省去了依赖范围scope。这些信息可以省略是因为b继承了a中的dependencyManagement配置,完整的依赖声明已经包含在父POM中,子模块只需要配置简单地groupId和artifactId就能获得对应的依赖信息,从而引入正确的依赖。

使用这种依赖管理机制似乎不能减少太多的POM配置,不过还是强烈推荐采用这种方法。其主要原因在于在父POm中使用dependencyManagement声明依赖能够统一项目范围中依赖的版本,当依赖本版在父POM中生命之后,子模块在使用依赖的时候就无需声明版本,也就不会发生多个子模块使用依赖版本不一致的情况。这可以帮助降低冲突的几率。

如果子模块不声明依赖的使用,即使该例来已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果。

使用import的依赖范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。例如想要在另外一个模块中使用dependencyManagement配置,除了复制配置或者继承这两种方式之外,还可以使用import范围依赖将这一配置导入,如下所示。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.test</groupId>
            <artifactId>a</artifactId>
            <version>1.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

注意,上述代码中依赖的type值为pom,import范围依赖由于其特殊性,一般都是指向打包类型为pom的模块。如果有多个项目,他们使用的依赖版本都是一致的,则就可以定义一个使用denendencyManagement专门管理依赖的POM,然后在各个项目中导入这些依赖管理配置。

插件管理

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

例如配置了maven-source-plugin,将其jar-no-fork目标绑定到了verity生命周期阶段,以生成项目源码包。如果一个项目中有很多子模块,并且需要得到所有这些模块的源码包,那么很显然,为所有模块重复类似的插件配置不是最好的办法。这时更好的方法是在父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>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

当子模块需要生成源码包的时候,只需要如下简单的配置。

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

子模块声明了maven-source-plugin插件,同时又继承了父模块的pluginManagement配置。

如果子模块不需要使用父模块中pluginManagement配置的插件,可以尽管将其忽略。如果子模块需要不同的插件配置,则可以自行配置以覆盖父模块的pluginManagement配置。

有了pluginManagement元素,子模块的POM也能得以简化了。

当项目中的多个模块有同样的插件配置时,应当将配置移到父POM的pluginManagement元素中。即使各个模块对于同一插件的具体配置不尽相同,也应当使用父POM的pluginManagement元素统一声明插件的版本。甚至可以要求所有用到的插件的版本在父POM的pluginManagement元素中声明,子模块使用插件时不配置版本信息,这么做可以统一项目的插件版本,避免潜在的插件不一致或者不稳定问题,也更易于维护。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值