Maven的聚合和继承
Maven的聚合和继承
聚合
聚合的目的是为了解决统一管理多个项目的生命周期;
例如一个大的系统可能拆分为很多个不同的子项目供不同的开发人员管理,如果项目之间相互独立开发,则每次开发测试部署都要单独执行mvn相关指令,如果项目过多则这个过程是非常繁琐的,因此引入聚合概念,将每个子系统整合为对应的子模块,统一管理这些子模块的生命周期,而子模块是不需要感知ROOT节点的存在,只需要关注自己的开发细节即可
1.构建思路
maven聚合:创建一个maven项目(主模块A), 用来管理其他的maven构件模块(子模块B、C、D),其核心配置参考:
<modules>
<module>模块1</module>
<module>模块2</module>
<module>模块n</module>
</modules>
<package>pom</package>
在主模块A中执行任何mvn命令,modules中包含的所有模块都会执行同样的命令,而被包含的模块不需要做任何特殊的配置(正常的Maven项目即可),module中配置的元素是被聚合模块pom.xml所在目录的相对路径
2.构建步骤
新建项目A(maven-module-A),配置maven路径
删除无用代码(src文件夹),随后在pom.xml中配置<package>
属性
分别创建maven-module-B、maven-module-C、maven-module-D模块(右键maven-module-A选择创建子模块),这些子模块都是通过A进行管理,不需要单独配置,只需要创建正常的Maven配置即可(此处聚合概念其Parent配置为none)
子模块创建完成,moduleA参考如下配置,刷新maven
子模块是默认生成的pom.xml,没做任何改动
测试mvn clean指令:moduleA执行clean指令,发现子模块也关联执行
测试mvn compile指令:
pom.xml中的module元素的值为被聚合的模块pom.xml所在的目录路径,可以是相对路*径,也可以是绝对路径,上面演示的是相对路径
聚合的功能中,聚合模块的pom.xml中通过 modules->module 来引用被聚合的模块,被聚合的模块是不用感知自己被聚合了,所以被聚合的模块中 pom.xml 中是不知道moduleA的存在的
继承
1.继承思路
基于上述聚合概念,当一个大型系统为了统一项目依赖版本管理,要求子系统引入的依赖要一致,则可能出现配置重复的情况,因此考虑将公共的信息提取出来。此处引入继承概念,由moduleA统一定义和管理依赖版本信息,然后子模块通过继承的方式获取到相应的版本信息即可
继承的实现在idea中的配置其实在上面已经有所体现,就是在创建子模块的时候有勾选父节点的选项,前面聚合默认是选择为None,如果选定的了是moduleA,则会构建moduleA和子模块的聚合和继承关系。也可基于上面聚合的基础上自定义配置引入继承概念
为了区分聚合和继承的目标,选择新建一个maven-module-parent作为父节点,其构建步骤参考如下:
1)创建父maven构件,统一定义依赖信息配置(统一管理maven依赖)
<dependencies>
<dependency>依赖的构件的坐标信息</dependency>
<dependency>依赖的构件的坐标信息</dependency>
<dependency>依赖的构件的坐标信息</dependency>
</dependencies>
2)将父构件的package元素设定为pom
<packaging>pom</packaging>
3)在子构件的pom.xml中引入父构件的配置
<parent>
<groupId>父构件groupId</groupId>
<artifactId>父构件artifactId</artifactId>
<version>父构件的版本号</version>
<relativePath>父构件pom.xml路径</relativePath>
</parent>
relativePath表示父构件pom.xml相对路径,默认是 ../pom.xml ,所以一般情况下父子结构的maven构件在目录结构上一般也采用父子关系
2.继承实现
参考实现
1)创建父构件,配置maven,修改package属性
<packaging>pom</packaging>
2)创建子构件
idea2024版本没有单独把聚合和继承拆开来,此处Parent选择None,然后手动 配置继承关系
3)父节点中引入maven依赖
<!-- 引入依赖配置 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
4)通过maven指令查看依赖构建关系(mvn dependency:tree
)
由结果可知,虽然child中没有配置依赖引入,但是其从父节点中继承了这些依赖配置
此处需注意relativePath配置:上述目录刚好满足父子目录关系,但是如果遇到不是这种情况的话,则需要配置relativePath参数,指定父pom.xml的正确位置,否则会报错。例如此处child在parent目录下,因此relativePath为../pom.xml
(标识父pom.xml位于子pom.xml的上一级目录),刚好符合这种关系所以可以省略relativePath配置
可以通过继承的元素
基于上述操作可知,groupId、version、dependency中的依赖 在子节点的pom.xml 中都没有写,这些都是从父pom.xml 中继承过来的,还有很多元素也可以被继承过来,参考说明如下
groupId:项目组ID,项目坐标的核心元素
version:项目版本,项目坐标的核心元素
description:项目的描述信息
organization:项目的组织信息
inceptionYear:项目的创始年份
url:项目的url地址
developers:项目的开发者信息
contributors:项目的贡献者信息
distributionManagement:项目的部署配置信息
issueManagement:项目的缺陷跟踪系统信息
ciManagement:项目的持续集成系统信息
scm:项目的版本控制系统信息
mailingLists:项目的邮件列表信息
properties:自定义的maven属性配置信息
dependencyManagement:项目的依赖管理配置
repositories:项目的仓库配置
build:包括项目的源码目录配置、输出目录配置、插件管理配置等信息
reporting:包括项目的报告输出目录配置、报告插件配置等信息
3.依赖管理(dependencyManagement)
基于上述操作,可以看到如果直接在父pom中定义dependencies配置,则其直接把所有的依赖传递给子节点。但有一种场景是,可能不同项目所需要引用的依赖不同(有些项目并不需要所有的依赖,可能只想用到其中一个依赖),但基于上述方式会把所有依赖都继承下来,造成项目的臃肿
为了解决上面这个场景问题,引入dependencyManagement属性,实现依赖声明管理。maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性,在dependencyManagement元素下声明依赖不会引入实际的依赖,它只是声明了这些依赖,不过它可以对dependencies中使用的依赖起到一些约束作用
构建步骤参考
1)修改父pom.xml,引入dependencyManagement
<!-- 引入依赖配置 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
</dependencies>
</dependencyManagement>
2)查看依赖树mvn dependency:tree
发现父子构件都看不到依赖的jar,说明在父pomx.xml中被dependencyManagement管理的依赖构建没有被子模块依赖进去
如果子模块想要使用这些配置,则可在子pom.xml中配置dependencies进行显示引用,这样依赖构建才会真正生效,例如在子pom.xml中配置引入一个spring框架,然后查看依赖树效果
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
由上述结果可知,parent没有列出依赖信息,而child列出相关的依赖信息
此外关注依赖旁边的红圈圈(父节点为向下表示传递,子节点为向上表示继承)
在子节点中的这个标识它是从父pom继承过来的依赖,所以如果没有指定version版本的时候其会引用父节点定义的版本
综上所述分析:dependency Management不会引入实际的依赖,只有在子类中使用dependency 来引入父dependencyManagement声明的依赖之后,依赖的构建才会被真正的引入。
使dependencyManagement来解决继承的问题,子pom.xml中只用写groupId,artifactId ,其他信息都会从父dependencyManagement 中声明的依赖关系中传递过来,从而确保version 版本的一致性。当版本升级需要修改则只需要修改父pom.xml版本即可。
4.单继承问题
基于前面dependencyManagement的引入,但是有个问题,只有使用继承的时候,dependencyManagement中声明的依赖才可能被子pom.xml用到,如果项目本来就有父pom.xml了,但是我现在想使用另外一个项目dependencyManagement中声明的依赖,此时应该怎么办?这就是单继承的问题,这种情况在spring-boot、spring-cloud中会遇到。
当想在项目中使用另外一个构件中dependencyManagement声明的依赖,而又不想继承这个项目的时候,可以在子项目中使用加入下面配置:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.noob.base</groupId>
<artifactId>maven-module-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>构件2</dependency>
<dependency>构件3</dependency>
<dependency>构件n</dependency>
</dependencies>
</dependencyManagement>
上述配置重点关注type(必须是pom)、scope(必须是import)
上述这个配置会将maven-module-parent中 dependencyManagement 元素中声明的所有依赖导入到当前pom.xml的 dependencyManagement 中,相当于把下面部分的内容
# 实现效果是替换成下面的内容
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>构件2</dependency>
<dependency>构件3</dependency>
<dependency>构件n</dependency>
</dependencies>
</dependencyManagement>
构建步骤
1)新建一个项目(maven-import),引入Jackson依赖,然后将其install到本地仓库
<dependencyManagement>
<dependencies>
<!-- Jackson Core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.1</version>
</dependency>
<!-- Jackson Databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
</dependencies>
</dependencyManagement>
2)在maven-module-child中使用maven-import中的这个依赖
因为maven-module-child已经继承了maven-module-parent,而maven是单继承机制,所以无法直接通过继承实现,因此可以考虑配置
<?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">
<!-- 定义父节点 -->
<parent>
<groupId>com.noob.base</groupId>
<artifactId>maven-module-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.noob.base</groupId>
<artifactId>maven-module-child</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.noob.base</groupId>
<artifactId>maven-import</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入父模块maven-module-parent中声明的spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- 引入maven-import中声明的jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>