Maven基础教程之继承

4次阅读

共计 8004 个字符,预计需要花费 21 分钟才能阅读完成。

这又是一个系列,一个要把 Maven 讲透的系列,希望能够对大家有帮助!

前言

上一篇文章就聚合进行了详细的总结,通过聚合,解决了我们构建多个项目的繁琐问题。但是,通过总结上一篇文章,大家可能会发现这么个问题,Project- A 和 Project- B 项目的 POM 文件,有很多相同的部分。通过以往的开发经验,如果多个模块有相同的部分,那就意味着我们可以把相同的部分抽取出来,作为公共的部分进行使用,比如在 Java 中,我们可以把相同的部分放在父类中,子类继承父类,就搞定了。在 Maven 的世界里,也有类似的机制能让我们提取出重复的配置,这就是 POM 的继承,而这篇文章就对 Maven 中的 POM 继承进行详细的总结。

小试牛刀

这里我还是将通过一个例子来了解一下 Maven 继承的初步使用配置。还是使用三个工程项目 Project-Parent、Project- C 和 Project- D 来进行说明,三个项目关系如下:

Project-Parent 工程 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.jellythink.ExtendDemo</groupId>
  <artifactId>Project-Parent</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <name>Project-Parent</name>

</project>

看这个 POM 文件,会发现和聚合有几分相像,只是没有 modules 节点,同样需要注意的是 packaging 的取值,必须使用 pom。

Project- C 的 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.jellythink.ExtendDemo</groupId>
    <artifactId>Project-Parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../Project-Parent/pom.xml</relativePath>
  </parent>

  <artifactId>Project-C</artifactId>

  <name>Project-C</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
  </dependencies>
</project>

Project- D 的 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.jellythink.ExtendDemo</groupId>
    <artifactId>Project-Parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../Project-Parent/pom.xml</relativePath>
  </parent>

  <artifactId>Project-D</artifactId>

  <name>Project-D</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
  </dependencies>
</project>

在 Project- C 和 Project- D 工程中都使用了 parent 元素声明父模块,parent下的坐标元素 groupIdartifactIdversion是必须的,它们指定了父模块的坐标;元素 relativePath 表示父模块 POM 的相对路径。在项目构建时,Maven 会首先根据 relativePath 检查父 POM。

同时,在 Project- C 和 Project- D 工程中,我们都没有在 POM 中指定 groupIdversion值,但是在构建时,依然可以成功构建。实际上,在 Project- C 和 Project- D 工程从父模块继承了这两个元素,这也就消除了一些不必要的配置。在这个例子中,子模块同父模块使用了同样的 groupIdversion值,如果遇到子模块需要使用和父模块不一样的 groupId 或者 version 的情况,我们则完全可以再子模块中显示声明。

可继承的 POM 元素

在上面,可以看到 groupIdversion是可以被继承的,那么还有哪些 POM 元素可以被继承呢?这里我将一些我们经常用作继承的元素做一下汇总,并进行简单的说明:

  • groupId:项目组 ID
  • version:项目版本
  • distributionManagement:项目的部署配置
  • properties:自定义的 Maven 属性
  • dependencies:项目的依赖配置
  • dependencyManagement:项目的依赖管理配置
  • build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等

依赖管理

上面说到 dependencies 是可以被继承的,那我们在 Project- C 和 Project- D 工程中很多公共的部分就可以提取出来,统一放到 Project-Parent 工程中去了,这样就可以移除公共配置,简化配置。

上面说的做法是可行的,但是会存在问题。我们考虑一下这样的一个场景。Project- C 和 Project- D 工程中公共的部分都提取到 Project-Parent 工程中去了,如果此时新增了一个 Project- E 工程,而 Project- E 工程有 80% 的依赖在 Project-Parent 工程中可以找到,另外那 20% 的依赖是个性化的;也就是说,Project-Parent 工程中有 20% 的依赖对于 Project- E 工程来说是没有用的。这个时候如果 Project- E 工程继承 Project-Parent 工程,则会引入无用的依赖,违背了 Maven 的使用原则。对于这种场景,Maven 中也有很完美的解决方案。

Maven 提供的 dependencyManagement 元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在 dependencyManagement 元素下的依赖声明不会引入实际的依赖,不过它能够约束 dependencies 下的依赖使用。现在将 Project-Parent 工程的 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.jellythink.ExtendDemo</groupId>
  <artifactId>Project-Parent</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <name>Project-Parent</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <springframework.version>5.1.6.RELEASE</springframework.version>
        <junit.version>4.11</junit.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <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>
        </dependencies>
    </dependencyManagement>
</project>

这里使用 dependencyManagement,将 springframework 和 junit 依赖提取了出来,放到了 Project-Parent 工程中。这样声明的依赖既不会给 Project-Parent 引入依赖,也不会给它的子模块引入依赖,不过这段配置是会被继承的。现在,我们将 Project- C 工程的 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.jellythink.ExtendDemo</groupId>
    <artifactId>Project-Parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../Project-Parent/pom.xml</relativePath>
  </parent>

  <artifactId>Project-C</artifactId>

  <name>Project-C</name>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
    </dependency>
    <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>
  </dependencies>
</project>

Project- D 项目和 Project- C 类似,这里不再累述。上面的 POM 中依赖配置比原来简单了不少,所有的 springframework 依赖只配置了 groupId 和 artifactId,省去了 version。这些依赖的配置都在 Project-Parent 工程中有了配置,子模块只需要简单的配置 groupId 和 artifactId 即可。如果在子模块不声明依赖的使用,即使该依赖已经在父 POM 的 dependencyManagement 中声明了,也不会产生任何实际的效果。

插件管理

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

总结

到这里,关于 Maven 中的继承概念就总结完了。其实关于 Maven 中的聚合和继承,还有一些比较冷门的知识点,我这里没有总结,比如反应堆的裁剪等,如果比较感兴趣,可以自行去学习。

期待五一假期!

果冻想,玩代码,玩技术!

2019 年 4 月 27 日,于内蒙古呼和浩特。


正文完
 0