文章目录
- 一、继承
- 1.1. 概念
- 1.2. 语法
- 1.3. 示例
- 1.4. 其他常见使用
- 二、聚合
- 2.1. 概念
- 2.2. 示例
- 三、属性
- 3.1. Java 系统属性
- 3.2.系统环境变量属性
- 3.3.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. 语法
使用父工程来管理依赖版本,一共就两步:
- 父工程中指定
<packaging>pom</packaging>
,并在<dependencyManagement>
节点下声明各个依赖的版本信息 - 子模块中通过
<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
路径下运行生命周期命令,来统一构建
到这里可能会发现聚合工程和继承用的父工程颇有相似的地方:
- 都需要创建一个单独的工程
- 都需要设置
<packaging>pom</packaging>
所以当既需要父工程又需要聚合工程的时候,为了避免冗余创建,基本都是只创建一个工程就可以,然后在该工程的 pom.xml
中将 <modules>
、<dependencyManagement>
等节点都添加进去。这种做法尤其适合一个项目分为多个模块的场景,但是公司级的父工程还是比较适合单独创建
下图为我个人比较喜欢的 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}
:
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>