Maven 项目之间的关系
一、继承
1、继承关系介绍
一个 Maven 项目 B 继承另外一个 Maven 项目 A ,那么 A 称为父项目, B 就称为子项目,所谓的项目之间的关系其实就是 pom 与 pom 之间的关系,(因为一个项目有且只有一个 pom.xml ,而 pom.xml 就是描述对应项目的)
使用场景:如果多个子项目中使用的是相同的依赖或插件,此时我们可以把相同的配置抽取到一个父项目中,进行统一的管理,保持一致性.
这里的管理具体是指:
- 子项目继承父项目的依赖,实现统一管理子项目有哪些依赖.
- 父项目管理依赖的版本,实现统一管理子项目依赖的版本.
值得一提的是,这里和全局变量 properties 有些不同, properties 常用来统一管理同一个项目中一类依赖的版本(例如统一管理 Spring 系列的 jar 包版本),而 Maven 继承常用来统一管理多个项目的同一个 jar 包版本
父子工程的项目,子项目最好是放在父项目的目录下,不要放在其它目录下,父项目不做任何逻辑处理,仅仅是为了管理所有的子项目,父项目里面只保留 pom.xml 就可以了.
2、IDEA 创建 Maven 项目
IDEA 创建 3 个 module ,名称分别为 parent、child01、child02
2.1、file---->new---->module
2.2、选择 Maven---->选择合适版本的 SDK---->选择合适的模板
我这里是利用 maven-archetype-quickstart 骨架创建一个 Java 项目
2.3、指定好相应配置
2.4、为了防止创建项目过慢,添加 archetypeCatalog=internal
2.5、child01、child02 创建方式和上面步骤相同,创建好的项目结构如下:
3、配置继承关系
我们这里配置成 child01、child02 都继承 parent 这个项目
这里说一下项目的打包方式, packaging 标签的取值有 jar (默认)、war、pom
打包方式:
jar: java 项目的打包方式,默认值.
war: web 项目的打包方式.
pom: 父项目的专有打包方式,该种方式,本项目不会被打包成 jar 或 war ,项目里 src 目录下代码无效(可删除), pom.xml 有效,只是作为其它项目的父项目使用.
3.1、设置父项目 pom.xml 配置打包方式为 pom
1 2 3 4 | <groupId>com.xiaomaomao.archetype</groupId> <artifactId>parent</artifactId> <version> 1.0 -SNAPSHOT</version> <packaging>pom</packaging> |
3.2、child01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>child01</artifactId> <version> 1.0 -SNAPSHOT</version> <!--子项目将来打包成 jar 包--> <packaging>jar</packaging> <name>child01</name> <!--使用 parent 标签表示该项目继承父项目,使用父项目具体坐标来引用父项目 --> <parent> <!--父项目的坐标--> <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>parent</artifactId> <version> 1.0 -SNAPSHOT</version> <!--指定父项目的 pom.xml 文件的相对物理路径, ../是上一级目录, ../parent/pom.xml:相对于当前pom.xml ,先到上一级,再到 parent 目录, 找到parent目录下的 pom.xml ,鼠标左键点击如果能跳到 parent 项目的 pom.xml 那么就配置对了 --> <relativePath>../parent/pom.xml</relativePath> </parent> |
3.3、child02
1 2 3 4 5 6 7 8 9 10 11 12 | <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>child02</artifactId> <version> 1.0 -SNAPSHOT</version> <packaging>jar</packaging> <name>child02</name> <parent> <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>parent</artifactId> <version> 1.0 -SNAPSHOT</version> <relativePath>../parent/pom.xml</relativePath> </parent> |
3.4、在 parent 的 pom.xml 中添加一些测试的依赖,child01、child02 pom.xml 中不配置任何依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <dependencies> <!--配置spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-version}</version> </dependency> <!--配置webmvc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 17 </version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version> 4.11 </version> <scope>test</scope> </dependency> </dependencies> |
3.5、依赖继承关系图
从图中可以很明显的看出, child01、child02 继承了 parent 中的依赖
4、父项目对子项目依赖的版本控制
举例: child01 模块使用 log4j 做日志记录,而 child02 模块使用 commons-logging 做日志记录(实际工作中并不会出现此种情况)
那么我们可以这样做
父工程中不引入 log4j 的依赖,也不引入 commons-logging 的依赖, child01 中声明使用 log4j
1 2 3 4 5 | <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 17 </version> </dependency> |
child02 中声明使用 commons-logging
1 2 3 4 5 | <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version> 1.0 </version> </dependency> |
这样我们就实现了在不同的子工程中引用自己所需要的依赖了,这样看上去很美好,但是呢这里会遗留下一个问题,如果有一天我们需要对各个子模块的依赖进行版本切换,我们只能找到对应的子模块,然后到各个子模块中去切换依赖的版本,这样很不方便,这个时候怎么办呢?
你可能想到了我们不是有父模块吗?让父模块对子模块的依赖进行统一管理就可以了,不错,我们是可以使用父模块,但是还是会有问题,为什么呢?
如果父模块中同时引入 log4j 和 commons-logging 的依赖,确实可以统一对子模块中的依赖进行管理
1 2 3 4 5 6 7 8 9 10 | <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 17 </version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version> 1.0 </version> </dependency> |
但是呢,这样做还是会存在一个问题, child01、child02 都会继承父模块中的依赖,这样原本只需要 log4j 的 child01 就会继承到自己不需要的 commons-logging,同理,child02 也会继承到 log4j ,这样的话
子模块中就很容易产生 jar 包的冲突,并且子模块会继承一些自己压根就不需要的 jar 包,那么怎么办呢?
这里我们可以使用 <dependencyManagement> 标签来解决我们上述的问题,这个标签的作用其实相当于一个对所依赖 jar 包进行版本管理的管理器.( dependencyManagement 里只是声明依赖,并不实现引入)
parent 中 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>parent</artifactId> <version> 1.0 -SNAPSHOT</version> <!--父项目的特有的打包方式,必须声明为 pom--> <packaging>pom</packaging> <name>parent</name> <properties> <project.build.sourceEncoding>UTF- 8 </project.build.sourceEncoding> <maven.compiler.source> 1.7 </maven.compiler.source> <maven.compiler.target> 1.7 </maven.compiler.target> <!--统一管理 Spring 系列的依赖的版本--> <spring-version> 5.2 . 8 .RELEASE</spring-version> </properties> <!--dependencies 中的依赖会被子项目继承--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring-version}</version> </dependency> </dependencies> <!--dependencyManagement 标签中声明的依赖,只是作为一个 jar 包统一管理的管理器 实际上该标签中声明的依赖不会被引入--> <dependencyManagement> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 17 </version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version> 1.0 </version> </dependency> </dependencies> </dependencyManagement> |
child 01 中 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>child01</artifactId> <version> 1.0 -SNAPSHOT</version> <!--子项目将来打包成 jar 包,默认值就是 jar--> <packaging>jar</packaging> <name>child01</name> <!--使用 parent 标签表示该项目继承父项目,使用具体坐标定位父项目--> <parent> <!--父项目的坐标--> <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>parent</artifactId> <version> 1.0 -SNAPSHOT</version> <!--指定父项目的 pom.xml 文件的相对物理路径, ../是上一级目录, ../parent/pom.xml:相对于当前pom.xml ,先到上一级,再到 parent 目录, 找到parent目录下的 pom.xml ,鼠标左键点击如果能跳到 parent 项目的 pom.xml 那么就配置对了 --> <relativePath>../parent/pom.xml</relativePath> </parent> <dependencies> <!--依赖的版本已经由父项目通过 dependencyManagement 进行统一管理了 如果没有注明版本:那么就会默认使用父项目中的版本 如果显示的声明了版本,就使用自己声明的版本--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </dependency> </dependencies> |
child02 中 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>child02</artifactId> <version> 1.0 -SNAPSHOT</version> <packaging>jar</packaging> <name>child02</name> <parent> <groupId>com.xiaomaomao.mavenAnalyse</groupId> <artifactId>parent</artifactId> <version> 1.0 -SNAPSHOT</version> <relativePath>../parent/pom.xml</relativePath> </parent> <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <!--子项目中显示的声明依赖版本,那么就使用子项目中声明的版本--> <version> 1.2 </version> </dependency> </dependencies> |
如上配置之后效果如下:
通过 dependencyManagement 标签,我们实现了最终的目的
子工程中相同的模块抽取到父工程的 dependencies 标签中,让每个子工程可以继承
子工程中不同的部分声明在父工程的 dependencyManagement 中,该标签中的依赖不会被父工程引用,但是使用了该标签可以对子模块中不同的依赖进行版本管理,例如:父工程中切换 log4j 和 commons-logging 的版本之后,如果子工程没有显示的声明版本,那么会随着父工程中版本的切换而同步切换
这里面还有一个 pluginManagement 标签,它是父工程对子工程插件版本的统一管理,和 dependencyManagement 的使用相同
二、聚合
聚合:将多个子项目添加到一个父项目中,然后通过对父项目进行操作( Maven 命令),从而实现对所有聚合的子项目的操作
例如:父项目执行 mvn package 操作,子项目也会被打包.
继承和聚合的区别
继承是告诉子项目它的父项目是谁,在哪里,聚合是告诉父项目它的子项目有哪些,分别在哪里
那么怎么实现聚合呢?
在父项目的 pom.xml 中配置
1 2 3 4 5 6 7 8 | <!--子项目中定位父项目使用的是 pom.xml 来定位的 而父项目定位子项目使用的名字来定位的,为什么不使用 pom.xml 来定位呢?个人猜测可能是同一路径下有多个子 项目,使用 pom.xml 不能准确的定位到是哪个子项目--> <modules> <module>../child01</module> <module>../child02</module> </modules> |
测试聚合后的效果
三、依赖关系
1、依赖关系的传递
项目A---->项目B---->项目C
概念:如果项目 A 依赖于项目 B ,项目 B 依赖于项目 C ,则项目 A 也依赖于项目 C ,这就叫做依赖的传递
如果觉得依赖这个词不是很好理解,可以把依赖翻译为使用,即项目 A 使用项目 B ,项目 B 使用项目 C
下面我们就来演示一个案例: child01 依赖 child02 , child02 依赖 child03 ,child03 依赖 log4j
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // child01 的 pom.xml <!--child01 依赖 child02--> <dependencies> <dependency> <groupId>com.xiaomaomao</groupId> <artifactId>child02</artifactId> <version> 1.0 -SNAPSHOT</version> </dependency> </dependencies> // child02 的 pom.xml <!--child02 依赖 child03--> <dependencies> <dependency> <groupId>com.xiaomaomao</groupId> <artifactId>child03</artifactId> <version> 1.0 -SNAPSHOT</version> </dependency> </dependencies> // child03 的 pom.xml <!--child03 依赖 log4j 1.2 . 12 --> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 12 </version> </dependency> </dependencies> |
查看依赖关系
从上图中可以看到,实现了依赖传递
2、控制依赖的传递
并不是所有的依赖都会传递
scope 为 compile 的依赖会发生传递
下面这些情况的依赖是不会传递的
- scope 为 test 的依赖不具有依赖传递性,但是具有继承性
- scope 为 provided 的依赖不具有依赖的传递性,但是具有继承性
- 配置 optional 标签为 true 的依赖不具有依赖传递性
修改 child03 pom.xml 中的相关依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 12 </version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version> 2.11 . 2 </version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version> 4.11 </version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version> 2.5 </version> <scope>provided</scope> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version> 2.0 . 5 </version> <!--optional 的默认值为 false --> <optional> true </optional> </dependency> </dependencies> |
查看依赖的传递性
3、依赖传递的原则
使用 Maven 不会出现 jar 包冲突,因为其通过两个原则来保证
3.1、就近原则,依赖的 jar 包距离本项目的层级越近(路径越短),优先级越高.
依赖配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // child01 中的 pom.xml <dependencies> <dependency> <groupId>com.xiaomaomao</groupId> <artifactId>child02</artifactId> <version> 1.0 -SNAPSHOT</version> </dependency> </dependencies> // child02 中的 pom.xml <dependencies> <dependency> <groupId>com.xiaomaomao</groupId> <artifactId>child03</artifactId> <version> 1.0 -SNAPSHOT</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 12 </version> </dependency> </dependencies> // child03 中的 pom.xml <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 17 </version> </dependency> </dependencies> |
查看结果
2、优先声明原则,在同一个 pom.xml 的 dependecy 标签里面, jar 包写在上面,优先级就越高.
依赖配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // child01 中的 pom.xml <dependencies> <dependency> <groupId>com.xiaomaomao</groupId> <artifactId>child02</artifactId> <version> 1.0 -SNAPSHOT</version> </dependency> </dependencies> // child02 中的 pom.xml <dependencies> <dependency> <groupId>com.xiaomaomao</groupId> <artifactId>child03</artifactId> <version> 1.0 -SNAPSHOT</version> </dependency> <dependency> <groupId>com.xiaomaomao</groupId> <artifactId>child04</artifactId> <version> 1.0 -SNAPSHOT</version> </dependency> </dependencies> // child03 中的 pom.xml <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 17 </version> </dependency> </dependencies> // child04 中的 pom.xml <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version> 1.2 . 12 </version> </dependency> </dependencies> |
查看结果
3、这两个原则的优先级关系?
首先按照就近原则,如果传递的依赖距离本项目的层级相同,那么再按照优先声明原则传递依赖
4、不要传递的依赖
在实际情况中有些依赖我们不希望传递,那么这些依赖该怎么处理呢?
4.1、如果当前的 pom 我们可以修改,使用 optional 标签即可
1 2 3 4 5 6 7 | <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version> 2.0 . 5 </version> <!--optional 的默认值为 false --> <optional> true </optional> </dependency> |
4.2、如果当前的 pom.xml 我们不能修改,使用 exclusion 标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version> 2.11 . 2 </version> <exclusions> <!--使用 exclusion 标签排除不需要的依赖--> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </exclusion> </exclusions> </dependency> </dependencies> |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~