国庆节10月1日
--


把继承与聚合讲透的一篇 —— 父工程怎么用 pom 打包方式加 parent 把依赖收上去、dependencyManagement 与 dependencies 有什么区别、properties 自定义属性怎么用花括号引用、modules 怎么一条命令构建全部模块,附本机实测的 Reactor Summary 和父子工程 pom 原文

91 篇把 tlias 拆成了 tlias-pojo、tlias-utils、tlias-web-management 三个模块,但拆完之后立刻冒出两个新问题:
这一篇(PPT 第 10-22 页)就是来收这两笔账的:继承解决依赖配置重复,聚合解决”一次构建全部模块”。PPT 第 11 页把这一节拆成四个小标题,本篇按这个顺序走:
| 小节 | 讲什么 | 对应 PPT |
|---|---|---|
| 继承 → 继承关系 | 父工程 / 子工程怎么建、配置怎么写 | 12-15 |
| 继承 → 版本锁定 | <dependencyManagement> 与 <properties> | 17-19 |
| 聚合 | <modules> 与”一次构建全部模块” | 21-22 |
PPT 第 12 页把三个子工程的 pom 并排放在一起,每个里面都有这么一段(三个模块里一模一样):
1<dependency>2 <groupId>org.projectlombok</groupId>3 <artifactId>lombok</artifactId>4 <version>1.18.34</version>5</dependency>6...页面上给这一页的批注只有两个字:繁琐。繁琐在哪:
| 麻烦 | 说明 |
|---|---|
| 重复写 | 同一段依赖在三个模块里各写一遍,将来再加一个模块还要再抄一遍 |
| 版本会不一致 | 三个地方各写各的版本号,某天只想改一个,很容易漏 —— 最后出现”pojo 用 1.18.30、utils 用 1.18.34”这种局面,排查起来很痛苦 |
| 共有依赖清单看不见 | 想知道”这个项目统一用哪些版本”,得把每个模块的 pom 都翻一遍 |
“共有依赖”的这种重复,正适合用继承来消掉。
PPT 第 13 页的三句话是这一节的骨架,先原样记住:
概念:继承描述的是两个工程间的关系,与 java 中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。 作用:简化依赖配置、统一管理依赖。 实现:
<parent> … </parent>。
把它画成图,就是”上面一个父工程,下面挂三个子工程”,父工程再往上还接着 SpringBoot 官方的父工程:
1spring-boot-starter-parent(SpringBoot 官方的父工程,管 SpringBoot 全家桶的版本)2 ↑ 继承3 tlias-parent(我们建的父工程:统一管理依赖、统一版本)4 ↑ 继承 ↑ 继承 ↑ 继承5 tlias-pojo tlias-utils tlias-web-management课件里的工程结构截图就是这个样子的(父工程和各子模块并列在一个 IDEA 工程里):

tlias-parent 与各子模块并列;图中除了本篇用到的 tlias-pojo、tlias-utils、tlias-web-management,还有课程后面继续拆出来的 tlias-web-system、tlias-web-report,它们同样是”认”tlias-parent 当父工程链子中间那层 tlias-parent 很关键:它自己也是别人的子工程(继承 spring-boot-starter-parent),同时又是三个业务模块的父工程 —— 这就是 Maven 的”继承链”。
父工程和普通模块长得一样(新建 Maven 模块),区别只有一个:打包方式。PPT 第 14 页给了 Maven 三种打包方式的对照表:
| 打包方式 | 含义 | 用在哪 |
|---|---|---|
| jar(默认) | 普通模块打包,springboot 项目基本都是 jar 包(内嵌 tomcat 运行) | 业务模块、工具模块,如 tlias-pojo、tlias-utils |
| war | 普通 web 程序打包,需要部署在外部的 tomcat 服务器中运行 | 传统 Web 项目 |
| pom | 父工程或聚合工程,该模块不写代码,仅进行依赖管理 | tlias-parent 这种”只管配置”的工程 |
父工程的 pom(课程代码 tlias-parent/pom.xml 原文):
1<parent>2 <groupId>org.springframework.boot</groupId>3 <artifactId>spring-boot-starter-parent</artifactId>4 <version>3.2.10</version>5 <!--父工程的pom.xml的相对路径-->6 <relativePath/> <!-- lookup parent from repository -->7</parent>8
9<groupId>com.itheima</groupId>10<artifactId>tlias-parent</artifactId>11<version>1.0-SNAPSHOT</version>12<packaging>pom</packaging>三个要点:
<packaging>pom</packaging> —— 这句话说明”我是一个父工程,不产出 jar”,去掉它默认就是 jar 打包;spring-boot-starter-parent —— SpringBoot 那一堆依赖的版本、编译插件配置都在它里面,继承过来就相当于”白拿一份官方版本清单”;<relativePath/> 写空 —— 意思是”别去上级目录找,直接去仓库里拿”,因为 spring-boot-starter-parent 是下载到本地仓库的第三方构件,不是我们工程目录里的模块。
<parent>(继承 spring-boot-starter-parent、<relativePath/> 写空),下半段是父工程自己的坐标与 <packaging>pom</packaging>PPT 第 15 页这张图里 spring-boot-starter-parent 的版本写的是 3.1.3,而课程代码 tlias-parent/pom.xml 里是 3.2.10 —— 同一份讲义里前后不一致。以课程代码的 3.2.10 为准(本机实验工程里也是 3.2.10),不要照着 PPT 的图手抄版本号。
每个子工程的 pom 里写一段 <parent> 指向父工程(课程代码 tlias-pojo/pom.xml 原文):
1<!--父工程-->2<parent>3 <groupId>com.itheima</groupId>4 <artifactId>tlias-parent</artifactId>5 <version>1.0-SNAPSHOT</version>6 <relativePath>../tlias-parent/pom.xml</relativePath>7</parent>8
9<artifactId>tlias-pojo</artifactId>10<version>1.0-SNAPSHOT</version>注意这段配置里没有 groupId —— 因为子工程配置了继承关系之后,坐标中的 groupId 是可以省略的,会自动继承父工程的(com.itheima)。PPT 第 15 页的那张截图专门把子工程 pom 里被”继承”影响的位置高亮了出来:

<parent> 里的三项坐标 + <relativePath> 指到父工程的 pom.xml;下半段子工程自己的坐标里,groupId 是可以省掉的(图中做了强调)子工程”认了父”之后,父工程里写的依赖会被子工程直接继承。课程代码里父工程放的是 lombok 和 spring-boot-starter:
1<!--直接引入依赖-->2<dependencies>3 <dependency>4 <groupId>org.projectlombok</groupId>5 <artifactId>lombok</artifactId>6 <version>${lombok.version}</version>7 <optional>true</optional>8 </dependency>9
10 <dependency>11 <groupId>org.springframework.boot</groupId>12 <artifactId>spring-boot-starter</artifactId>13 <version>${spring.boot.starter.version}</version>14 </dependency>15</dependencies>写完之后,三个子工程里原来那几段重复的 lombok 依赖就可以删掉了 —— 它们已经”继承”到了。
| 细节 | 说明 |
|---|---|
| groupId 可省略 | 子工程配置了继承关系之后,坐标中的 groupId 会自动继承父工程的,可以不写 |
| relativePath | 指定父工程的 pom 文件的相对位置;如果不指定,将从本地仓库/远程仓库查找 —— 所以父工程还没 install 到本地仓库时,子工程找不到父工程就会报错(这也是”父工程先构建一次”的原因) |
| 子工程版本优先 | 若父子工程都配置了同一个依赖的不同版本,以子工程的为准(父工程只管”默认值”,子工程想覆盖就自己写一行 <version>) |
| PPT 的问题 | 答案 |
|---|---|
| Maven 的继承关系实现步骤? | ① 创建父工程,设置打包方式为 pom,并继承 spring-boot-starter-parent;② 在子工程中配置继承关系;③ 在父工程中配置各个工程的共有依赖 |
<dependencyManagement>(PPT 第 17 页)#继承只解决了”所有模块都要用的依赖”。可事实是:OSS 的 SDK 只有 tlias-utils 用、JJWT 也只有它用 —— 这类”只有部分模块用”的依赖,如果直接写进父工程的 <dependencies>,就会强行塞给所有子工程(pojo 里根本用不到 OSS,却凭空多出一堆 jar)。
PPT 第 17 页给的解法是版本锁定:
在 maven 中,可以在父工程的 pom 文件中通过
<dependencyManagement>来统一管理依赖版本。
父工程里写成”版本清单”:
1<dependencyManagement>2 <dependencies>3 <!--JWT令牌-->4 <dependency>5 <groupId>io.jsonwebtoken</groupId>6 <artifactId>jjwt</artifactId>7 <version>0.9.1</version>8 </dependency>9 </dependencies>10</dependencyManagement>子工程要用的时候,只写 groupId 和 artifactId、不写版本:
1<dependencies>2 <dependency>3 <groupId>io.jsonwebtoken</groupId>4 <artifactId>jjwt</artifactId>5 </dependency>6</dependencies>版本从哪来?就从父工程那份清单里”对号入座”。这样版本仍然只有一处(父工程),但依赖不会强塞给不用的模块。
版本锁定是两件事配合:父工程给”版本清单”(<dependencyManagement>)+ 子工程自己写依赖(不写版本)。子工程如果既没写 <version>、父工程的清单里也没有它,Maven 会直接报错,提示依赖的 version 缺失。
PPT 第 17 页的示意图右上角还标了一个 0.9.2 的版本号,讲的就是”版本被别人改掉/写岔了”这种情况 —— 对应的是第 15 页那条规则:父子工程都配了同一个依赖的不同版本时,以子工程的为准。所以在用了 <dependencyManagement> 统一版本之后,子工程里就不要再手写版本号,免得又出现”清单里是 0.9.1、实际用的是别的版本”。
<dependencyManagement> 与 <dependencies> 的区别(PPT 第 19 页)#这是这一节最容易被问到的对比题,PPT 第 19 页的答案原文是:
<dependencies>是直接依赖,在父工程配置了依赖,子工程会直接继承下来。<dependencyManagement>是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)。
展开成一张对照表:
| 对比项 | <dependencies> | <dependencyManagement> |
|---|---|---|
| 它是什么 | 直接依赖(真的引入 jar) | 统一管理依赖版本(一份”版本清单”,本身不引入 jar) |
| 写在父工程时,子工程的表现 | 子工程直接继承下来,什么都不用写就能用 | 子工程不会自动引入这个依赖 |
| 子工程还要不要写依赖 | 不用写 | 要写(写在子工程的 <dependencies> 里,只写 groupId + artifactId) |
| 子工程要不要写版本 | 不用(跟着父工程) | 不写(版本从父工程的清单里拿;写了就是”以子工程为准”) |
| 适合放什么 | 所有模块都要用的依赖(lombok、spring-boot-starter) | 只有部分模块用的依赖(OSS SDK、jaxb、jjwt) |
| 一句话记法 | “送上门”:父工程写一次,子工程人人有份 | “给清单”:父工程只管版本,用不用子工程自己说 |
课程代码里两者是同时出现的:lombok 和 spring-boot-starter 走”直接引入”,OSS/jaxb/jjwt 走”版本清单”,正好和上面表格的判断标准对得上。
版本集中在父工程之后,还有个小问题:版本号还是散落在 <dependency> 和 <dependencyManagement> 的各个 <version> 里。PPT 第 18 页用自定义属性把版本号也提出来(写进 <properties>),再用 ${} 引用:
1<properties>2 <lombok.version>1.18.30</lombok.version>3 <jjwt.version>0.9.1</jjwt.version>4</properties>引用时写 ${属性名}:
1<dependencies>2 <dependency>3 <groupId>org.projectlombok</groupId>4 <artifactId>lombok</artifactId>5 <version>${lombok.version}</version>6 </dependency>7</dependencies>1<dependencyManagement>2 <dependencies>3 <!--JWT令牌-->4 <dependency>5 <groupId>io.jsonwebtoken</groupId>6 <artifactId>jjwt</artifactId>7 <version>${jjwt.version}</version>8 </dependency>9 </dependencies>10</dependencyManagement>好处很直白:版本号只有一处,升级时改 <properties> 里那一行就行;而且打开父工程 pom,一眼就能看到”这个项目都用了哪些版本”。属性名(lombok.version、jjwt.version)是自己起的,课程惯例是 <artifactId>.version。
PPT 第 18 页这个例子里的 lombok 版本写的是 1.18.30,而课程代码 tlias-parent/pom.xml 里是 1.18.34(PPT 第 12 页那三个重复的 lombok 依赖也是 1.18.34)—— 还是那句话,版本以课程代码为准,PPT 这页只是演示”属性怎么声明、怎么引用”。
课程代码里父工程一共声明了 7 个自定义属性:
1<properties>2 <maven.compiler.source>17</maven.compiler.source>3 <maven.compiler.target>17</maven.compiler.target>4 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>5
6 <!--自定义属性-->7 <lombok.version>1.18.34</lombok.version>8 <spring.boot.starter.version>3.2.10</spring.boot.starter.version>9 <aliyun.sdk.version>3.17.4</aliyun.sdk.version>10 <jaxb.api.version>2.3.1</jaxb.api.version>11 <activation.version>1.1.1</activation.version>12 <jaxb.runtime>2.3.3</jaxb.runtime>13 <jjwt.version>0.9.1</jjwt.version>14</properties>其中上面三个(maven.compiler.*、编码)是前面章节就一直在用的”内置属性”,下面七个才是这一节讲的自定义属性。
继承解决的是”配置重复”,聚合解决的是”构建麻烦”。
聚合:将多个模块组织成一个整体,同时进行项目的构建。 聚合工程:一个不具有业务功能的”空”工程(有且仅有一个 pom 文件)。 作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)。
实现方式是父工程里写 <modules>,把要一起构建的模块列出来:
1<!--聚合-->2<modules>3 <module>../tlias-pojo</module>4 <module>../tlias-utils</module>5 <module>../tlias-web-management</module>6</modules><module> 里写的是模块的相对路径(../tlias-pojo 表示”上一级目录里的 tlias-pojo 模块”),Maven 顺着这个路径找到那个模块的 pom,就把它纳入本次构建。
注意:聚合工程中所包含的模块,在构建时,会自动根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关。(PPT 第 21 页)
也就是说:<modules> 里写的顺序不重要,Maven 会自己算出”谁先谁后”——先构建被依赖的模块(pojo、utils),最后构建依赖别人的模块(web-management)。想改变构建顺序,只能改变依赖关系,改书写顺序没用。
有意思的是,课程里的 tlias-parent 同时干了两件事:它既是三个模块的父工程(管依赖、管版本),又是它们的聚合工程(一条命令构建全部)——这正是 PPT 第 22 页说的”常将两种关系制作到同一个 pom 文件中”。
本机实验工程 tlias-modules 就是上面这套配置的真实版本(tlias-parent 里同时有 <modules> 聚合、<properties> 自定义属性、<dependencyManagement> 版本锁定),在父工程上执行一次:
1mvn clean install -DskipTests本机实测(Maven 3.9.14 / JDK 17)—— 完整输出(含模块耗时):
1[INFO] Building tlias-parent 1.0-SNAPSHOT [1/4]2[INFO] Building tlias-pojo 1.0-SNAPSHOT [2/4] → 打出 tlias-pojo-1.0-SNAPSHOT.jar3[INFO] Building tlias-utils 1.0-SNAPSHOT [3/4] → 打出 tlias-utils-1.0-SNAPSHOT.jar4[INFO] Building tlias-web-management 0.0.1-SNAPSHOT [4/4] → 打出可执行 jar5[INFO] Reactor Summary:6[INFO] tlias-parent .......................... SUCCESS [ 0.249 s]7[INFO] tlias-pojo ............................ SUCCESS [ 3.361 s]8[INFO] tlias-utils ........................... SUCCESS [ 2.097 s]9[INFO] tlias-web-management ................. SUCCESS [ 5.956 s]10[INFO] BUILD SUCCESS Total time: 11.982 s输出里那段 Reactor Summary(构建反应堆汇总)就是聚合的直接证据:一次执行,四个模块按 parent → pojo → utils → web-management 的顺序全部 SUCCESS([1/4]~[4/4] 是 Maven 给本次构建里的每个模块编的序号),整个构建总耗时 11.982 s。
两条实测结论:
① 聚合——一条命令、四个模块全构建,顺序是 Maven 按模块间依赖关系自动排的(tlias-web-management 依赖 pojo 和 utils,所以它排最后),不用手动一个个构建;
② 分模块后的相互引用落地了——tlias-pojo、tlias-utils 各自打出 jar 并装进本地仓库,tlias-web-management 就是以依赖的方式引用它们(install 之后,别的工程也能从本地仓库拿到这两个模块)。
PPT 第 22 页用一问一答把两者放在一起对比:
联系:继承与聚合都属于设计型模块,打包方式都为 pom,常将两种关系制作到同一个 pom 文件中。 区别:继承用于简化依赖配置、统一管理依赖版本,是在子工程中配置继承关系;聚合用于快速构建项目,是在**父工程(聚合工程)**中配置聚合的模块。
把这段展开讲透 —— 为什么说它们”像”:
| 相同点 | 说明 |
|---|---|
| 都属于设计型模块 | 都不是业务模块:不写业务代码,存在的意义是”管配置、管构建” |
| 打包方式都是 pom | 都要写 <packaging>pom</packaging>,都不产出可运行的 jar |
| 常写在同一份 pom 里 | 课程里 tlias-parent 就是”父工程 + 聚合工程”二合一:<parent> 让它继承别人、<modules> 让它聚合子模块 |
为什么说它们”不一样”(这是最容易混的地方,按”方向 + 配在哪 + 干什么”三条来分):
| 对比项 | 继承 | 聚合 |
|---|---|---|
| 关系方向 | 子 → 父:子工程主动”认父”,从父工程那里拿配置 | 父 → 子:聚合工程主动把子模块”收进来”一起构建 |
| 配在哪 | 子工程的 <parent> | 父工程的 <modules> |
| 解决什么问题 | 简化依赖配置、统一管理依赖版本 | 快速构建项目(一次构建全部模块) |
| 不配会怎样 | 各模块的依赖各写一遍,版本容易写岔 | 构建要按依赖顺序一个个手动来,容易漏、容易错 |
| 和对方的关系 | 严格说两件事是独立的:子工程不参与聚合也能继承 | 聚合不要求模块之间有继承关系,只是课程里把它们写在了一起 |
一句话总结:继承管”配置”(写代码时少写、版本统一),聚合管”构建”(打包时一条命令全搞定);两者常常落在同一个 pom 上,但一个关系的配置在子工程,另一个在父工程 —— 考试/面试问”区别”,先答这一句,再补”都属于设计型模块、打包都是 pom、常写在同一个 pom 里”。
| 问题 | 答案 |
|---|---|
| 继承是什么? | 两个工程间的关系,与 Java 中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承;实现靠 <parent> |
| 继承的作用? | 简化依赖配置、统一管理依赖 |
| 继承关系实现步骤? | ① 创建父工程,打包方式设为 pom,并继承 spring-boot-starter-parent;② 在子工程中配置继承关系;③ 在父工程中配置各个工程共有的依赖 |
| 打包方式有哪三种? | jar(普通模块,SpringBoot 项目基本都是 jar,内嵌 tomcat 运行)、war(普通 web 程序,要部署到外部 tomcat)、pom(父工程或聚合工程,不写代码,仅做依赖管理) |
子工程写 <parent> 的三个细节? | groupId 可省略(自动继承父工程);relativePath 指定父工程 pom 的相对位置(不指定就从本地仓库/远程仓库找);父子都配了同一依赖的不同版本时以子工程为准 |
| 版本锁定怎么做? | 父工程用 <dependencyManagement> 写”版本清单”(不直接引入),子工程在自己的 <dependencies> 里写依赖、不写 <version> |
<dependencies> 与 <dependencyManagement> 的区别? | 前者是直接依赖、父工程配了子工程直接继承;后者只统一管理版本、不会直接依赖,还要在子工程中引入所需依赖(无需指定版本) |
| 自定义属性怎么写? | 在 <properties> 里声明(如 <lombok.version>1.18.30</lombok.version>),用 ${lombok.version} 引用 |
| 聚合是什么?怎么实现? | 将多个模块组织成一个整体,同时进行项目的构建;聚合工程是”不具有业务功能的空工程(有且仅有一个 pom 文件)“;用 <modules> 列出子模块,作用是快速构建项目 |
| 聚合的构建顺序谁定? | Maven 自动按模块间的依赖关系排,与 <modules> 里书写的顺序无关(本机实测:parent → pojo → utils → web-management) |
| 继承与聚合的联系? | 都属设计型模块、打包方式都是 pom、常写在同一个 pom 文件里 |
| 继承与聚合的区别? | 继承用于简化依赖配置、统一管理依赖版本,配置在子工程;聚合用于快速构建项目,配置在父工程(聚合工程) |
<parent> … </parent>pom,并继承 spring-boot-starter-parent;② 在子工程中配置继承关系;③ 在父工程中配置各个工程共有的依赖<parent> 的三个细节:groupId 可省略(自动继承父工程的);relativePath 指定父工程 pom 的相对位置(不指定就从本地仓库/远程仓库查找);父子工程都配了同一个依赖的不同版本时,以子工程的为准<dependencyManagement> 统一管理依赖版本;它不会直接依赖,子工程还要自己引入依赖,只是不用写版本<dependencies> 与 <dependencyManagement> 的区别:<dependencies> 是直接依赖,父工程配了子工程直接继承下来;<dependencyManagement> 是统一管理依赖版本,不会直接依赖,需要子工程引入所需依赖(无需指定版本)<properties> 里声明(如 <lombok.version>1.18.30</lombok.version>、<jjwt.version>0.9.1</jjwt.version>),用 ${lombok.version} 引用<modules>tlias-parent → tlias-pojo → tlias-utils → tlias-web-management,总耗时 11.982 s)2-1 做一个”只管配置”的父工程 现在三个子模块里都重复写着同一份 lombok 依赖,你想把它收上去。请写出这个父工程的 pom 片段:
org.springframework.boot:spring-boot-starter-parent:3.2.10,注意它不在你工程目录里,要去仓库里拿);com.itheima:tlias-parent:1.0-SNAPSHOT)。
(练习文件 test_92_继承与聚合.xml 里已经给了写作区。)一级 · 思路:父工程的第一个标志就是”打包方式”——不写代码的工程该打成什么;第二个标志是”它自己也是别人的子工程”
二级 · 方法:用 <packaging> 声明打包方式(值取三种类型里”不写代码”的那一个);用 <parent> 继承 spring-boot-starter-parent,并且把 <relativePath> 写空(表示去仓库找,不是在上级目录找)
三级 · 骨架:<packaging>____</packaging>;<parent> … <relativePath/> </parent>
1<parent>2 <groupId>org.springframework.boot</groupId>3 <artifactId>spring-boot-starter-parent</artifactId>4 <version>3.2.10</version>5 <!--父工程的pom.xml的相对路径-->6 <relativePath/> <!-- lookup parent from repository -->7</parent>8
9<groupId>com.itheima</groupId>10<artifactId>tlias-parent</artifactId>11<version>1.0-SNAPSHOT</version>12<packaging>pom</packaging>说明:<packaging>pom</packaging> 说明”父工程/聚合工程,不写代码、仅做依赖管理”;<relativePath/> 写空是因为 spring-boot-starter-parent 是从仓库下载的第三方构件,上级目录里并没有它。这份片段就是课程代码 tlias-parent/pom.xml 的开头部分(PPT 第 15 页那张图上的版本号写的是 3.1.3,以课程代码的 3.2.10 为准)。
2-2 让子工程”认父”
已经有一个父工程 com.itheima:tlias-parent:1.0-SNAPSHOT,它就放在上一级目录的 tlias-parent 文件夹里。请给子工程 tlias-pojo 写出 pom 片段:
groupId 还需要写吗?为什么?
(练习文件 test_92_继承与聚合.xml 里已经给了写作区。)一级 · 思路:子工程”认父”是写在子工程 pom 里的;“父工程的位置”有专门的标签来指
二级 · 方法:<parent> 里写三个坐标,再加 <relativePath> 指向父工程的 pom.xml;坐标三项里 groupId 可以省,因为会自动继承父工程的
三级 · 骨架:<parent> … <relativePath>../____/pom.xml</relativePath> </parent>,然后子工程自己只写 <artifactId> 和 <version>
1<!--父工程-->2<parent>3 <groupId>com.itheima</groupId>4 <artifactId>tlias-parent</artifactId>5 <version>1.0-SNAPSHOT</version>6 <relativePath>../tlias-parent/pom.xml</relativePath>7</parent>8
9<artifactId>tlias-pojo</artifactId>10<version>1.0-SNAPSHOT</version>groupId:子工程配置了继承关系之后,坐标中的 groupId 会自动继承父工程的(这里就是 com.itheima)。另外 <relativePath> 的作用是”指定父工程 pom 文件的相对位置”,不写的话 Maven 会去本地仓库/远程仓库找父工程 —— 父工程还没 install 过就会报错。 2-3 让三个子模块共用同一份依赖版本管理
场景:OSS 的 SDK(com.aliyun.oss:aliyun-sdk-oss:3.17.4)和 JJWT(io.jsonwebtoken:jjwt:0.9.1)只有工具类模块用得到,实体类模块、业务模块都不需要;但版本必须三处一致、只在一处改。请:
tlias-utils)里的配置,让它用上这两个依赖但不写版本号;${} 引用。
(练习文件 test_92_继承与聚合.xml 里已经给了写作区。)一级 · 思路:父工程要写的是”版本清单”而不是”依赖”——这两者在 Maven 里是两个不同的标签;子工程再自己写依赖、只写前两项坐标
二级 · 方法:父工程用 <dependencyManagement> 包一层 <dependencies>(注意是”管理”不是”引入”);版本号抽到 <properties> 里用 <名字.version> 风格命名,然后用 ${名字.version} 引用
三级 · 骨架:<properties><aliyun.sdk.version>____</aliyun.sdk.version></properties>;父工程 <dependencyManagement><dependencies><dependency>…<version>${____}</version></dependency></dependencies></dependencyManagement>;子工程 <dependencies><dependency>…(不写 version)</dependency></dependencies>
tlias-parent/pom.xml,节选,和课程代码一致):
1<properties>2 <!--自定义属性-->3 <aliyun.sdk.version>3.17.4</aliyun.sdk.version>4 <jjwt.version>0.9.1</jjwt.version>5</properties>6
7<!--统一管理依赖的版本-->8<dependencyManagement>9 <dependencies>10 <dependency>11 <groupId>com.aliyun.oss</groupId>12 <artifactId>aliyun-sdk-oss</artifactId>13 <version>${aliyun.sdk.version}</version>14 </dependency>15
16 <!--JWT-->17 <dependency>18 <groupId>io.jsonwebtoken</groupId>19 <artifactId>jjwt</artifactId>20 <version>${jjwt.version}</version>21 </dependency>22 </dependencies>23</dependencyManagement>tlias-utils/pom.xml,节选)——只写 groupId + artifactId,不写 version:
1<dependencies>2 <!--阿里云OSS依赖-->3 <dependency>4 <groupId>com.aliyun.oss</groupId>5 <artifactId>aliyun-sdk-oss</artifactId>6 </dependency>7
8 <!--JWT-->9 <dependency>10 <groupId>io.jsonwebtoken</groupId>11 <artifactId>jjwt</artifactId>12 </dependency>13</dependencies><dependencies> 直接放父工程?那样会把 OSS、JJWT 强行塞给 tlias-pojo、tlias-web-management,它们根本用不到;<dependencyManagement> 只给”版本清单”,用不用由子工程自己决定 —— 这就是 PPT 第 19 页那两句的区别。 2-4 一条命令构建全部模块
三个子模块和一个父工程(分别在 ../tlias-pojo、../tlias-utils、../tlias-web-management)现在要”一起构建”。请:
test_92_继承与聚合.xml 里已经给了写作区。)一级 · 思路:把多个模块”组织成一个整体、同时构建”这件事叫聚合,配置写在父工程里;至于谁先谁后,先看模块之间”谁依赖谁”
二级 · 方法:父工程用 <modules> 列子模块,每项写模块的相对路径;构建顺序由依赖关系决定,不由书写位置决定
三级 · 骨架:<modules><module>../____</module>…</modules>;顺序:被依赖的在前
1<!--聚合-->2<modules>3 <module>../tlias-pojo</module>4 <module>../tlias-utils</module>5 <module>../tlias-web-management</module>6</modules>tlias-parent(父工程自己)→ tlias-pojo → tlias-utils → tlias-web-management。本机实测(Maven 3.9.14 / JDK 17)在父工程上执行一次 mvn clean install -DskipTests,输出正是 [1/4] tlias-parent → [2/4] tlias-pojo → [3/4] tlias-utils → [4/4] tlias-web-management,四个模块全部 SUCCESS,Total time: 11.982 s。tlias-web-management 依赖 pojo 和 utils,所以它必须排在最后 —— 想改顺序只能改依赖关系。2-5 说清”直接引入”和”版本管理”的区别 用一句话各自概括下面两种写法的效果,并说明它们分别适合放什么依赖:
一级 · 思路:一句话概括时抓住”送上门 / 给清单”这个区别:前者子工程自动拿,后者子工程自己点名
二级 · 方法:判断标准是”所有模块都要用”还是”部分模块才用”
三级 · 骨架:第 1 种 = <dependencies>(直接依赖、子工程直接继承);第 2 种 = <dependencyManagement>(统一管理版本、子工程引入时不写版本)
<dependencies>(直接依赖):父工程配好之后,子工程会直接继承下来,什么额外配置都不用写。<dependencyManagement>(统一管理依赖版本):它不会直接依赖,只是”版本清单”;子工程还要在自己的 <dependencies> 里引入所需依赖,只是无需指定版本(版本从清单里拿)。@Data、@Slf4j),所以放 <dependencies> 一次配好、人人有份,省事;OSS SDK 和 JJWT 只有 tlias-utils 用得到,放 <dependencies> 会把它们强行塞给所有模块(下载更多 jar、传递依赖也更多),所以放 <dependencyManagement> 只给版本清单,用不用由子工程自己说。 3-1 给一套多模块工程写全”继承 + 版本锁定 + 聚合”
场景:tlias 拆成了 tlias-pojo、tlias-utils、tlias-web-management 三个子模块,要新建一个父工程 tlias-parent 把它们管起来。按下面 6 步做:
org.springframework.boot:spring-boot-starter-parent:3.2.10),打包方式表明它不写代码;1.18.34、spring-boot-starter 3.2.10),版本用自定义属性引用;com.aliyun.oss:aliyun-sdk-oss:3.17.4、javax.xml.bind:jaxb-api:2.3.1、javax.activation:activation:1.1.1、org.glassfish.jaxb:jaxb-runtime:2.3.3、io.jsonwebtoken:jjwt:0.9.1;tlias-pojo)的 pom:配置继承关系(父工程在上一级目录),并回答”groupId 为什么可以不写”;<dependencies> 与 <dependencyManagement> 的区别;② 继承与聚合的联系与区别。
(练习文件 test_92_继承与聚合.xml 里按这 6 步给了写作区。)涉及知识点
| 知识点 | 在这里的应用 |
|---|---|
父工程与 pom 打包 | <packaging>pom</packaging>、继承 spring-boot-starter-parent |
| 子工程继承 | 子工程 <parent> + <relativePath> + groupId 可省略 |
| 共有依赖 | 父工程 <dependencies>(lombok、spring-boot-starter) |
| 版本锁定 | 父工程 <dependencyManagement> + 子工程不写版本 |
| 自定义属性 | <properties> 里的 ${xxx.version} |
| 聚合 | 父工程 <modules>、构建顺序由依赖关系决定 |
一级 · 思路:父工程的三块内容是”往上继承(<parent>)、往下管理(<dependencies> + <dependencyManagement>)、往下聚合(<modules>)“,各写各的、别混在一起
二级 · 方法:版本全部走 <properties>;“所有模块都用”的依赖放 <dependencies>,“只有部分模块用”的放 <dependencyManagement>;构建顺序按”被依赖的先构建”
三级 · 骨架:<packaging>____</packaging> / <properties>…</properties> / <dependencies>…</dependencies> / <dependencyManagement><dependencies>…</dependencies></dependencyManagement> / <modules>…</modules>
1~3. 父工程 tlias-parent/pom.xml(与课程代码一致,省略 XML 声明与命名空间):
1<parent>2 <groupId>org.springframework.boot</groupId>3 <artifactId>spring-boot-starter-parent</artifactId>4 <version>3.2.10</version>5 <relativePath/> <!-- 去仓库里拿 SpringBoot 官方父工程 -->6</parent>7
8<groupId>com.itheima</groupId>9<artifactId>tlias-parent</artifactId>10<version>1.0-SNAPSHOT</version>11<packaging>pom</packaging> <!-- 父工程/聚合工程:不写代码,仅做依赖管理 -->12
13<properties>14 <maven.compiler.source>17</maven.compiler.source>15 <maven.compiler.target>17</maven.compiler.target>16 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>17
18 <!--自定义属性-->19 <lombok.version>1.18.34</lombok.version>20 <spring.boot.starter.version>3.2.10</spring.boot.starter.version>21 <aliyun.sdk.version>3.17.4</aliyun.sdk.version>22 <jaxb.api.version>2.3.1</jaxb.api.version>23 <activation.version>1.1.1</activation.version>24 <jaxb.runtime>2.3.3</jaxb.runtime>25 <jjwt.version>0.9.1</jjwt.version>26</properties>27
28<!--直接引入依赖:所有子工程都继承(人人有份)-->29<dependencies>30 <dependency>31 <groupId>org.projectlombok</groupId>32 <artifactId>lombok</artifactId>33 <version>${lombok.version}</version>34 <optional>true</optional>35 </dependency>36
37 <dependency>38 <groupId>org.springframework.boot</groupId>39 <artifactId>spring-boot-starter</artifactId>40 <version>${spring.boot.starter.version}</version>41 </dependency>42</dependencies>43
44<!--统一管理依赖的版本:只给清单,用不用子工程自己说-->45<dependencyManagement>46 <dependencies>47 <dependency>48 <groupId>com.aliyun.oss</groupId>49 <artifactId>aliyun-sdk-oss</artifactId>50 <version>${aliyun.sdk.version}</version>51 </dependency>52
53 <dependency>54 <groupId>javax.xml.bind</groupId>55 <artifactId>jaxb-api</artifactId>56 <version>${jaxb.api.version}</version>57 </dependency>58 <dependency>59 <groupId>javax.activation</groupId>60 <artifactId>activation</artifactId>61 <version>${activation.version}</version>62 </dependency>63 <!-- no more than 2.3.3-->64 <dependency>65 <groupId>org.glassfish.jaxb</groupId>66 <artifactId>jaxb-runtime</artifactId>67 <version>${jaxb.runtime}</version>68 </dependency>69
70 <!--JWT-->71 <dependency>72 <groupId>io.jsonwebtoken</groupId>73 <artifactId>jjwt</artifactId>74 <version>${jjwt.version}</version>75 </dependency>76 </dependencies>77</dependencyManagement>tlias-pojo/pom.xml:
1<!--父工程-->2<parent>3 <groupId>com.itheima</groupId>4 <artifactId>tlias-parent</artifactId>5 <version>1.0-SNAPSHOT</version>6 <relativePath>../tlias-parent/pom.xml</relativePath>7</parent>8
9<artifactId>tlias-pojo</artifactId>10<version>1.0-SNAPSHOT</version>groupId 可以不写:子工程配置了继承关系之后,坐标中的 groupId 会自动继承父工程的(com.itheima);同样地,lombok、spring-boot-starter 也不用再写,它们从父工程的 <dependencies> 里继承下来了。1<!--聚合-->2<modules>3 <module>../tlias-pojo</module>4 <module>../tlias-utils</module>5 <module>../tlias-web-management</module>6</modules>tlias-parent → tlias-pojo → tlias-utils → tlias-web-management(本机实测在父工程上执行 mvn clean install -DskipTests,输出为 [1/4]~[4/4] 四个模块全 SUCCESS,Total time: 11.982 s;顺序不是书写顺序,而是模块间依赖关系决定的)。<dependencies> 是直接依赖,父工程配了依赖,子工程会直接继承下来;<dependencyManagement> 是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)。
② 联系:继承与聚合都属于设计型模块、打包方式都为 pom、常将两种关系制作到同一个 pom 文件中(tlias-parent 就是这样)。区别:继承用于简化依赖配置、统一管理依赖版本,是在子工程中配置继承关系;聚合用于快速构建项目,是在父工程(聚合工程)中配置聚合的模块。如果你喜欢,那么欢迎来到我的世界!
了解更多暂未播放



"所有的相遇都是久别重逢。"—— 未知


