Maven 继承、聚合、属性


一、继承

1.1. 概念


当项目较大,为了便于开发和管理,经常需要将工程划分成多个 Maven 工程(这些 Maven 工程此时被称为模块),这时模块间很可能会相互依赖,而每个模块的依赖信息都是模块单独管理,所以很可能导致依赖冲突,如下图:

模块间依赖冲突

很明显上图中,Lombok 模块发生了依赖冲突,虽然这种情况 Maven 仲裁机制会自动帮我们判定留下的版本是 Service 模块中的 Lombok 1.10.0,但如果 Dao 模块引用 Lombok 1.18.8 的目的就是为了使用 1.18.8 的新特性,这个时候就会有问题,因为最后留下来的版本 1.10.0 并不具备新特性

为了避免上述问题的发生,所以不建议在各个模块中单独管理依赖的版本信息,建议用一个专门的工程来统一管理依赖的版本,通常的情况下,所谓父工程,就是这个专门做依赖版本信息管理的工程

不只是多模块的场景适合父工程,建议每个公司都要有自己的一个公司级别的父工程,在工程中将多年总结下来的依赖间的版本搭配定义下来,之后有新项目启动时,都来继承这个父工程,减少版本兼容性的试验,


1.2. 语法


使用父工程来管理依赖版本,一共就两步:

  1. 父工程中指定 <packaging>pom</packaging>,并在 <dependencyManagement> 节点下声明各个依赖的版本信息
  2. 子模块中通过 <parent> 声明父工程的 GAV,之后依赖引用时不再需要 <version> 信息

1.3. 示例


(1)先定义一个父工程,其 pom.xml 如下:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<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.ares5k</groupId>
  <artifactId>ares5k-parent</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!--  修改打包方式 -->
  <packaging>pom</packaging>

  <!-- 管理依赖的版本 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.8</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

<dependencyManagement> 节点下声明的依赖信息,并不会真正的将依赖引用进来,这里只是声明依赖版本而已


(2)再定义一个子模块,其 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <!-- 工程坐标 -->
  <artifactId>ares5k-module</artifactId>

  <!-- 指定父工程坐标-->
  <parent>
    <groupId>com.ares5k</groupId>
    <artifactId>ares5k-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../ares5k-parent/pom.xml</relativePath>
  </parent>

  <!-- 引入 lombok 依赖,不需要指定 version 信息 -->
  <dependencies>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
  </dependencies>
</project>

详细说明:

  • 当子模块的 <groupId><version> 与父工程相同时,在子模块中就可以将子模块的 <groupId><version> 省略

  • <relativePath> 用相对路径指定父工程 pom.xml 文件的位置,如果父工程已经安装在本地或远程仓库中,那么 <relativePath> 可以省略,<relativePath> 声明时,其寻找父工程的路径为 relativePath -> 本地仓库 -> 远程仓库

  • 子模块 <dependencies> 中的声明才是真正的把依赖引入进来,子模块如果不主动引入依赖,那么父工程 <dependencyManagement> 节点下声明的依赖就不会被真正的引入

1.4. 其他常见使用


在父工程中,除了管理依赖的版本,也经常用来管理生命周期插件、自定义属性、部署仓库、远程仓库等

自定义属性

自定义属性的使用方式:在 <properties> 下自定义节点,节点内的数据就属性值,定义后不管是父工程还是子模块,直接通过 ${自定义节点名} 就可以直接访问自定义属性

<!-- 声明自定义属性 -->
<properties>
  <ares5k-lombok-version>1.18.8</ares5k-lombok-version>
  <ares5k-jar-plugin>3.0.2</ares5k-jar-plugin>
</properties>
<!-- 使用自定义属性 -->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>${ares5k-lombok-version}</version>
</dependency>

部署仓库 <distributionManagement> 和 远程仓库 <repositories> 在父工程声明后,子模块会直接继承该配置,不需要再声明。生命周期插件管理 <pluginManagement>,网上很多人说它和 <dependencyManagement> 一样,在父工程中声明后,如果子模块不主动引入,那么子模块就不会应用父工程 <dependencyManagement> 中配置的插件,这个说法,我测试是不正确的,最起码在 Maven 3.6.3 版本中不是这样的,只要在父工程 <dependencyManagement> 中声明了插件,不管子模块是否主动引入,在执行生命周期插件时,都会应用父工程中配置的插件


二、聚合


2.1. 概念

当有很多 Maven 工程都需要执行生命周期命令时,如果一个一个执行很浪费时间,所以可以通过创建一个聚合工程,将所有的 Maven 工程聚合到一起,然后统一执行命令

聚合

2.2. 示例

聚合工程就是一个普通的 Maven 工程,只不过需要设置 <packaging>pom</packaging>,然后在 <modules> 节点中将子模块目录的相对路径添加进来即可,<module> 中不是子模块的 <artifactId> 而是其与聚合工程的相对路径

<packaging>pom</packaging>
<modules>
  <module>../ares5k-module-test</module>
  <module>ares5k-module-inner</module>
</modules>

<modules> 添加好后就可以在聚合工程的 pom.xml 路径下运行生命周期命令,来统一构建

统一构建

到这里可能会发现聚合工程和继承用的父工程颇有相似的地方:

  1. 都需要创建一个单独的工程
  2. 都需要设置 <packaging>pom</packaging>

所以当既需要父工程又需要聚合工程的时候,为了避免冗余创建,基本都是只创建一个工程就可以,然后在该工程的 pom.xml 中将 <modules><dependencyManagement> 等节点都添加进去。这种做法尤其适合一个项目分为多个模块的场景,但是公司级的父工程还是比较适合单独创建

下图为我个人比较喜欢的 Maven 结构:
喜欢的Maven结构


三、属性


我们除了可以在 pom.xml 中使用自定义属性,也可以使用已有属性,已有属性大概分为:Java 系统属性、系统环境变量属性、Maven 内置属性

3.1. Java 系统属性

Java 系统属性包括:Java 默认的系统属性、运行 Java 程序时传入的虚拟机参数、Java 程序中自定义的系统属性,其使用语法很简单,就是 ${Java系统属性名}

打印 Java 系统属性列表

如果不知道 Java 系统属性都有什么,可以通过下面 Java 代码打印一下:

package com.ares5k;

import java.util.Properties;
import java.util.Set;

public class App {
    public static void main(String[] args) {
        Properties properties = System.getProperties();
        Set<Object> propNameSet = properties.keySet();
        for (Object propName : propNameSet) {
            String propValue = properties.getProperty((String) propName);
            System.out.println(propName + " = " + propValue);
        }
    }
}

运行上面代码后,会有类似如下结构的输出,= 左边的就是 Java 系统属性名,右边的是 Java 系统属性值,假如在 pom.xml 中使用 ${java.runtime.name},在 pom 文件运行时,就会将内容替换成 Java(TM) SE Runtime Environment

java.runtime.name = Java(TM) SE Runtime Environment
sun.boot.library.path = D:\java8\jre\bin
java.vm.version = 25.251-b08
java.vm.vendor = Oracle Corporation
java.vendor.url = http://java.oracle.com/
path.separator = ;
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
file.encoding.pkg = sun.io
user.country = CN

3.2.系统环境变量属性

系统环境变量就是我们在操作系统中设置的环境变量,以 Windows 为例,下图就是系统变量,在 pom.xml 中通过语法 ${env.全大写系统变量名} 的方式就可以使用系统环境变量值,以下图中系统变量 ComSpec 为例,应该写成 ${env.COMSPEC}

Win系统变量


3.3.Maven 内置属性

除了能获取 Java 系统变量、操作系统环境变量,Maven 还提供了内置的属性

(1)访问 Pom 文件的节点

通过 ${project.标签名}${project.父标签.子标签} 可以访问 pom.xml 中的节点,不只可以访问当前工程 pom.xml 中的节点,超级 Pom 中的节点也可以访问

测试后,发现并不是所有的节点都能访问,列举一些我实验可用的:

属性说明
${project.basedir}项目在磁盘的全路径
${project.packaging}项目的打包方式
${project.groupId}项目的 groupId
${project.artifactId}项目的 artifactId
${project.version}项目的 version
${project.parent.groupId}父工程的 groupId
${project.parent.artifactId}父工程的 artifactId
${project.parent.version}父工程的 version
${project.parent.relativePath}到父工程目录的相对路径
${project.build.finalName}Maven 执行 package 阶段后的包名
${project.build.directory}构建后的输出目录
${project.build.sourceDirectory}源程序的目录
${project.build.outputDirectory}源程序编译后的输出目录
${project.build.testSourceDirectory}测试源程序的目录
${project.build.testOutputDirectory}测试源程序编译后的输出目录
${project.distributionManagement.repository.url}远程部署仓库中发版库的 URL
${project.distributionManagement.snapshotRepository.url}远程部署仓库中快照库的 URL

(2)访问Settings 文件的节点

通过 ${settings.标签名}${settings.父标签.子标签} 可以访问 Maven 安装目录/conf/settings.xml 中的节点,跟访问 Pom 文件一样,并不是所有节点都能成功访问,但是感觉 settings.xml 文件中没啥好总结的,就这样吧,就不列出了

(3)时间戳

可以通过 ${maven.build.timestamp} 获取当前时间,一般常用在打包后设置包名

如果对时间格式不满意,可以添加如下属性
<maven.build.timestamp.format>自定义时间格式</maven.build.timestamp.format> 来修改时间格式:

<!-- 自定义时间格式 -->
<properties>
  <maven.build.timestamp.format>yyyy-MM-dd</maven.build.timestamp.format>
</properties>
<!-- 自定义包名 -->
<build>
  <finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
</build>

修改后通过 ${maven.build.timestamp} 就可以获取目标格式的自定义时间了,但是有一点要注意的是,通过这种方式获取的时间的时区是 UTC,并且无法修改时区,如果想修改时区,我们就不能用 ${maven.build.timestamp} 这种方式来获取时间,而是需要引入一个生命周期插件,代码如下:

<build>
  <plugins>
    <!-- 引入插件 -->
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>build-helper-maven-plugin</artifactId>
      <version>3.3.0</version>
      <executions>
        <execution>
          <goals>
            <goal>timestamp-property</goal>
          </goals>
          <configuration>
            <!-- 自定义名称,后面输出日期时使用 -->
            <name>build.time</name>
            <!-- 设置日期格式 -->
            <pattern>yyyyMMddHHmmss</pattern>
            <!-- 设置时区 -->
            <timeZone>GMT+8</timeZone>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
  <!-- 设置打包名:artifactId + version + 上面设置的时间自定义名称 -->
  <finalName>${project.artifactId}-${project.version}-${build.time}</finalName>
</build>


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值