第二十二章. Maven 架构:RVP 5.x 依赖管理与构建解析

第二十二章. Maven 架构:RVP 5.x 依赖管理与构建解析

摘要:本章我们将深入 RVP 5.x 架构的核心——pom.xml 文件。我们将逐一解析根 POM、dependencyManagement(依赖管理)、ruoyi-common-bom(物料清单)以及 spring-boot-maven-plugin(打包插件)的配置与协同工作机制,帮助你彻底理解 5.x 版本“优雅、简洁、易于维护”的 Maven 依赖管理哲学。

本章学习路径

我们将按照以下路径,从根配置到子模块,层层解开 RVP 5.x 的 Maven 架构:

Maven 架构(依赖管理与构建解析)


22.1. 根 pom.xml:RVP 5.x 架构的“总指挥部”

在 RVP 5.x 的项目结构中,位于 RuoYi-Vue-Plus/pom.xml 的根 pom.xml 文件是整个项目的“总指挥部”。它不包含任何业务代码,其核心职责是 定义规范、聚合模块、管理环境

22.1.1. 坐标 (GAV) 的身份标识

我们首先查看根 pom.xml 顶部的基础配置:

1
2
3
4
5
6
7
8
9
<project ...>
<modelVersion>4.0.0</modelVersion>

<groupId>org.dromara</groupId>
<artifactId>ruoyi-vue-plus</artifactId>
<version>${revision}</version>

<packaging>pom</packaging>
</project>
  • GAV 坐标groupId, artifactId, version 是 Maven 项目的唯一坐标。
    • groupId: org.dromara,项目所属的组织。
    • artifactId: ruoyi-vue-plus,项目名称。
    • version: ${revision},一个变量,我们将在 22.2 节深入解析。
  • <modelVersion>4.0.0</modelVersion>:指定了 Maven 的项目对象模型(POM)版本,固定为 4.0.0
  • <packaging>pom</packaging>[核心] 此标签声明了这是一个“父项目”。它的职责 不是 打包成 jarwar,而是“聚合”与“管理”在 <modules> 标签中定义的子模块。

22.1.2. <modules>:聚合“四大模块”

packagingpom 的项目,通过 <modules> 标签来指定它所管理的子模块:

1
2
3
4
5
6
<modules>
<module>ruoyi-admin</module>
<module>ruoyi-common</module>
<module>ruoyi-extend</module>
<module>ruoyi-modules</module>
</modules>

这四行配置与项目根目录下的四个核心文件夹一一对应。Maven 在构建根项目时,会根据这个列表,依次进入每个子模块目录执行构建。RVP 5.x 的分层架构(Web 入口、通用插件、扩展服务、业务模块)在 Maven 层面得到了清晰的体现。

22.1.3. <properties>:集中管理(一)

<properties> 标签是 RVP 5.x 优雅管理依赖的基石。它允许我们定义“变量”,然后在整个项目(包括所有子模块)中引用。

1
2
3
4
5
6
7
<properties>
<revision>5.5.1</revision>
<spring-boot.version>3.5.7</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
</properties>

这里定义了关键的环境和框架版本:

  • <java.version>17</java.version>:在 <build> 插件中会引用此变量,确保项目所有模块都使用 Java 17 进行编译。
  • <spring-boot.version>3.5.7</spring-boot.version>:在 <dependencyManagement> 中会引用此变量,确保所有 Spring Boot 相关的依赖都使用 3.5.7 版本。

这种集中管理的方式,使得未来升级 Spring Boot 版本或 Java 版本时,只需修改根 pom.xml 中的这一行配置即可。

22.1.4. <properties>:集中管理(二)

<properties> 标签的另一个重要职责是统一管理所有第三方依赖的版本号。

1
2
3
4
5
6
7
8
9
<properties>
<mybatis.version>3.5.16</mybatis.version>
<satoken.version>1.44.0</satoken.version>
<mybatis-plus.version>3.5.14</mybatis-plus.version>
<hutool.version>5.8.40</hutool.version>
<redisson.version>3.51.0</redisson.version>
<snailjob.version>1.8.0</snailjob.version>
<lombok.version>1.18.40</lombok.version>
</properties>

在 RVP 4.x 时代,这些版本号分散在各个子模块的 pom.xml 中,升级 sa-token 可能需要修改多个文件。

在 5.x 中,所有版本被集中定义在根 POM。任何子模块(如 ruoyi-demo)需要使用 hutool 时,只需引入 hutool 的 GAV,version 会自动从根 POM 继承,确保了 版本统一极大简化了维护

22.1.5. <profiles>dev vs prod 环境隔离与激活

<profiles> 标签用于定义不同环境(如开发、生产)下的特定配置。

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
<profiles>
<profile>
<id>local</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
<activation>
<!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
<logging.level>warn</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
</profiles>

RVP 5.x 通过 profiles 实现了 Maven 构建环境与 Spring Boot 配置文件的联动:

  1. <id>dev</id>:定义了 dev 环境。
  2. <activeByDefault>true</activeByDefault>:使 dev 成为默认激活的环境。在 IDEA 中打开项目时,此 profile 会被自动勾选。
  3. <properties><profiles.active>dev</profiles.active></properties>[核心机制]dev profile 被激活时,Maven 会设置一个名为 profiles.active 的属性,其值为 dev
  4. Spring Boot 联动:Spring Boot 的 application.yml 文件会读取这个 Maven 属性(@profiles.active@),并将其作为 spring.profiles.active 的值,从而指示 Spring Boot 加载 application-dev.yml 配置文件。

22.2. [核心] 破解 RVP 5.x 版本管理(上):${revision}

在 22.1 节中,我们分析了根 pom.xml 的基础配置,并注意到 GAV (GroupId, ArtifactId, Version) 中的 <version> 被设置为了一个变量:${revision}。但在 4.x 版本的 RVP 中,版本号是“硬编码”在每一个子模块的 pom.xml 文件中的。本节我们将深入探讨 RVP 5.x 为什么要做出这种改变,以及 ${revision} 变量是如何生效的。

22.2.1. 4.x 时代的痛点:子模块版本号“硬编码”的维护难题

在 RVP 4.x 的架构中,每个子模块(如 ruoyi-admin, ruoyi-system)的 pom.xml 文件中,都明确地“硬编码”了自身的版本号,例如 <version>4.8.1</version>

这种方式的 维护痛点 非常明显:当项目需要从 4.8.1 升级到 4.8.2 时,开发者必须 手动修改 所有子模块 pom.xml 文件中的 <version> 标签。在一个拥有几十个模块的大型项目中,这是一项繁琐且极易出错的工作。一旦遗漏了某个模块,就会导致父子模块版本不一致,引发严重的构建失败或依赖冲突。

22.2.2. 5.x 的解决方案:GAV 中的 <version>${revision}</version>

RVP 5.x 彻底解决了这个痛点。在根 pom.xml (RuoYi-Vue-Plus/pom.xml) 中,我们看到:

1
2
3
<groupId>org.dromara</groupId>
<artifactId>ruoyi-vue-plus</artifactId>
<version>${revision}</version>

它使用了 Maven 的属性占位符 ${revision} 作为版本号。更重要的是,所有子模块(如 ruoyi-admin)都从这个根 POM 继承:

1
2
3
4
5
<parent>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId>
<version>${revision}</version>
</parent>

通过这种方式,所有模块的版本号都 唯一 指向了根 POM 中定义的 ${revision} 变量。

22.2.3. ${revision} 的定义:在 <properties> 中统一定义 5.5.1

这个 ${revision} 变量的值是在哪里定义的呢?答案就在根 pom.xml<properties> 标签中:

1
2
3
4
<properties>
<revision>5.5.1</revision>
<spring-boot.version>3.5.7</spring-boot.version>
</properties>

Maven 在解析 POM 时,会使用 <properties> 块中的 <revision>5.5.1</revision>,替换掉 GAV 中的 ${revision} 占位符。

这种“统一定义、多处引用”的机制,实现了当项目需要从 5.5.1 升级到 5.6.0 时,我们 只需要修改根 pom.xml<revision> 标签内的值,整个项目(包含所有子模块)的版本号就会自动、同步更新。

22.2.4. flatten-maven-plugin:让 ${revision} 占位符生效的“魔法”

我们面临一个新问题:${revision} 变量在 Maven 构建时被正确解析了。但是,当我们将项目 install (安装) 到本地仓库或 deploy (部署) 到私服时,其他项目如何“知道”${revision} 究竟代表 5.5.1 呢?

如果 Maven 只是简单地把 pom.xml(包含 ${revision} 变量)部署上去,那么其他项目在依赖 RVP 时,会因为无法解析 ${revision} 变量而失败。

为了解决这个问题,RVP 引入了一个关键插件:flatten-maven-plugin(扁平化插件)。

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
<!-- 统一版本号管理 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>

22.2.5. flatten-maven-plugin 工作原理:process-resources 阶段的替换

这个插件的工作原理如下:

  1. <phase>process-resources</phase>:插件被绑定在 Maven 构建生命周期的 process-resources 阶段(一个较早的阶段)。
  2. “扁平化” (Flatten):当构建执行到此阶段时,flatten 插件会被触发。它会读取原始的 pom.xml(包含 ${revision} 变量)。
  3. “解析” (Resolve):它将 POM 中所有的变量和占位符(如 ${revision}${spring-boot.version})替换为它们在 <properties> 中定义的 静态值(如 5.5.13.5.7)。
  4. “生成新 POM”:该插件会生成一个 新的、“扁平化”的 POM 文件(例如 target 目录下的 .flattened.pom.xml)。这个新 POM 文件中 不包含任何变量,只有解析后的静态值。
  5. <updatePomFile>true</updatePomFile>:此配置项告诉插件,用这个新生成的“扁平化” POM,去 替换 Maven 后续构建和部署(package, install, deploy)中使用的 POM。

因此,我们最终安装(install)到本地 Maven 仓库或部署(deploy)到私服的 pom.xml,是一个 已经将 ${revision} 解析为 5.5.1 的“干净”版本,从而保证了其他项目的正确依赖。


22.3. 根 pom.xmldependencyManagement

在 22.2 节中,我们解决了 RVP 5.x 的版本号管理机制。现在,我们进入根 pom.xml 中最庞大、也是最核心的标签:<dependencyManagement>

22.3.1. [核心] 依赖管理的“两阶段”

在大型多模块项目中,最大的噩梦之一就是“依赖冲突”。例如,ruoyi-demo 模块引入了 hutool-core-5.8.1,而 ruoyi-system 模块引入了 hutool-core-5.8.2,这会导致最终打包时出现版本冲突,引发不可预知的错误。

为了解决这个痛点,Maven 提供了 <dependencyManagement> 标签。它将依赖管理分为了“两阶段”:

  1. 阶段一:声明
  • 位置:在 pom.xml<dependencyManagement> 标签内。
    * 作用只声明“如果要用 hutool,它的版本 必须5.8.40”。
    * 效果不会 为任何模块(包括根模块)实际引入 hutool 的 jar 包。它只是一个“版本规则”。
  1. 阶段二:引入 (Dependencies)
  • 位置:在 子模块(如 ruoyi-demo)的 <dependencies> 标签内。
    * 作用实际引入 hutool 依赖。
    * 效果:开发者 只需 提供 groupIdartifactId,无需(也不应)提供 <version>。Maven 会自动向上查找根 POM 的 <dependencyManagement>,并使用那里声明的 5.8.40 版本。

RVP 5.x 架构正是利用这一机制,在根 pom.xml 中对所有核心技术栈的版本进行了“中央声明”。

22.3.2. RVP 核心技术栈声明

<dependencyManagement> 标签内部声明了 RVP 5.x 所使用的核心技术栈。我们可以将其分为两大类:BOM 导入直接声明

RVP 5.x 所依赖的核心技术栈及其在框架中的作用如下表所示:

技术分类依赖项 (ArtifactId)核心价值与在 RVP 中的作用
核心框架spring-boot-dependenciesSpring Boot 3。提供整个框架的 IoC、AOP、Web 等基础能力。
安全框架sa-token-spring-boot3-starterSa-Token。替代 Spring Security,负责登录认证、权限校验、Token 管理。
持久层mybatis-plus-spring-boot3-starterMyBatis-Plus。MyBatis 增强工具,提供强大的 CRUD 操作和数据权限等功能。
多数据源dynamic-datasource-spring-boot3-starterDynamic-DS。提供动态多数据源切换能力,支持读写分离。
缓存redisson-spring-boot-starterRedisson。作为 Redis 客户端,提供分布式缓存、分布式锁等服务。
分布式锁lock4j-redisson-spring-boot-starterLock4j。基于 Redisson 实现的分布式锁解决方案。
分布式调度snail-job-client-starterSnailJob。RVP 5.x 引入的分布式任务调度中心(客户端)。
API 文档springdoc-openapi-starter-webmvc-apiSpringDoc。替代 Swagger 2,用于自动生成 OpenAPI 3 规范的 API 文档。
对象映射mapstruct-plus-spring-boot-starterMapStruct-Plus。在编译期自动生成 VO, BO, DTO 之间的转换代码。
工作流warm-flow-mybatis-plus-sb3-starterWarm-Flow。国产工作流引擎,用于处理业务流程(如审批流)。
系统监控spring-boot-admin-starter-serverSpring Boot Admin。提供 ruoyi-monitor 模块,用于监控应用健康。
第三方登录JustAuthJustAuth。集成在 ruoyi-common-social 中,用于实现 Gitee 等第三方登录。
工具包hutool-bomHutool。Java 工具库,提供日期、字符串、IO 等常用工具方法。

22.3.3. 策略一:<scope>import</scope> (BOM 导入)

分析根 pom.xml<dependencyManagement>,我们能看到 RVP 5.x 依赖管理的第一个高级技巧:使用 <scope>import</scope> 来导入 BOM (Bill of Materials)。

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
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-bom</artifactId>
<version>${hutool.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-bom</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
  • BOM (物料清单):BOM 文件本身是一个 pom.xml,它 只包含 一个巨大的 <dependencyManagement> 列表,用于声明一整套框架(如 Spring Boot)所有组件的版本。
  • <type>pom</type><scope>import</scope>:这两个标签组合使用,告诉 Maven:“请把 spring-boot-dependencies 这个 POM 文件中所有的 <dependencyManagement> 声明,原封不动地导入到我(根 pom.xml)的 <dependencyManagement> 中。

RVP 5.x 通过这个机制,实现了:

  1. 导入 Spring Boot BOM:RVP 自动继承了 Spring Boot 官方管理的所有依赖版本。
  2. 导入 Hutool BOM:RVP 自动继承了 Hutool 官方管理的所有工具包版本。
  3. 导入 ruoyi-common-bom:RVP 导入了 自己 的 BOM,用于管理 ruoyi-common-core 等所有内部通用模块的版本。我们将在 22.5 节深入解析它。

22.3.4. 策略二:直接声明与 <exclusions> 依赖排除

对于 BOM 未能覆盖的依赖(如 Sa-Token),RVP 5.x 采用了“直接声明”的方式,即在 <dependencyManagement> 中明确指定其 GAV。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>${satoken.version}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>${satoken.version}</version>
<exclusions>
<exclusion>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</exclusion>
</exclusions>
</dependency>
  • 版本引用:所有 <version> 标签都引用了 22.1 节中 <properties> 里定义的变量,如 ${satoken.version}
  • <exclusions>:这是一个主动的“排雷”机制。sa-token-jwt 默认会依赖一个 hutool-all 包,而 RVP 5.x 提倡的是按需引入 hutool 的子模块(如 hutool-core)。为了避免 hutool-all 带来的潜在冲突和臃肿,RVP 在声明 sa-token-jwt 时,通过 <exclusions> 标签 主动排除了 它对 hutool-all 的依赖。

22.4. 根 pom.xml 构建配置 (<build>)

在 22.3 节中,我们解析了 <dependencyManagement> 如何“声明”依赖。现在,我们转向根 pom.xml<build> 标签。此标签定义了项目如何被编译、测试、以及如何处理资源文件。这些配置同样会被所有子模块继承。

22.4.1. maven-compiler-plugin:指定 Java 17 编译

build 块中的第一个核心插件是 maven-compiler-plugin,它负责将 .java 源码编译为 .class 字节码。

1
2
3
4
5
6
7
8
9
10
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
  • <source>${java.version}</source>:指定了源码的 JDK 版本。${java.version} 变量在 <properties> 中被定义为 17
  • <target>${java.version}</target>:指定了编译后 .class 文件应兼容的 JVM 版本,同样是 17
  • <encoding>${project.build.sourceEncoding}</encoding>:指定了编译时使用 UTF-8 编码,防止中文乱码。

22.4.2. annotationProcessorPaths:Lombok 与 MapStruct-Plus 的编译期处理

maven-compiler-plugin 配置内部,有一个至关重要的 <annotationProcessorPaths> (注解处理器路径) 标签。

RVP 框架大量使用了“编译期注解”。这些注解(如 Lombok 的 @Data)不是在“运行”时生效,而是在“编译”时触发 Java 代码的自动生成。

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
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>${therapi-javadoc.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${mapstruct-plus.lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>

此配置块告诉 maven-compiler-plugin:在编译时,请加载并运行以下处理器:

  1. Lombok:用于自动生成 getter/setter、构造函数等。
  2. Spring Boot:用于处理 @ConfigurationProperties,生成 spring-configuration-metadata.json 文件(为 YML 提供智能提示)。
  3. MapStruct-Plus:用于自动生成 VO, DTO, BO 之间的映射转换实现类。
  4. lombok-mapstruct-binding:一个“粘合剂”插件,确保 Lombok 生成的方法能被 MapStruct-Plus 正确识别。

22.4.3. <compilerArgs>-parameters</compilerArgs>:为反射保留参数名

1
2
3
4
5
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>

默认情况下,Java 编译后的 .class 文件会擦除方法的参数名(例如 findUser(String userName) 会变为 findUser(String arg0))。

痛点:这导致了 Spring MVC, MyBatis 等依赖“反射”的框架,无法通过参数名自动绑定值,开发者必须手动添加 @Param("userName") 这样的注解,非常繁琐。

解决方案<arg>-parameters</arg> 告诉编译器(javac):“请在 .class 文件中保留方法的参数名”。这使得 RVP 中的 Spring 和 MyBatis 代码可以大量减少 @Param@RequestParam 的使用,让代码更简洁。

22.4.4. resources 插件:application.yml 变量替换

<resources> 标签用于控制 src/main/resources 目录下的文件如何被处理。RVP 5.x 采用了一种“两段式”的资源处理策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>application*</include>
<include>bootstrap*</include>
<include>banner*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
  1. 第一段 (filtering: false):首先,Maven 复制 src/main/resources 下的 所有 文件,并且 关闭过滤filtering: false)。这是为了保证二进制文件(如 ip2region.xdb 数据库)在复制过程中不会因“变量替换”而被损坏。
  2. 第二段 (filtering: true):接着,Maven 再次 处理 src/main/resources 目录,但这次:
    • includes:只包含 application*bootstrap*banner* 这几类文件。
    • filtering: true开启过滤

最终效果:在 application.yml 中,我们可以使用 Maven 变量 @profiles.active@(注意是 @ 符号,而非 $)。当 dev profile 激活时,filtering: true 会将 application.yml 中的 @profiles.active@ 替换为 静态字符串 dev,从而激活 Spring Boot 的 dev 环境。

22.4.5. repositories:配置华为云 Maven 仓库

pom.xml 文件末尾的 <repositories><pluginRepositories> 标签,用于指定 Maven 下载依赖的远程仓库地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<repositories>
<repository>
<id>public</id>
<name>huawei nexus</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
</pluginRepository>
</pluginRepositories>

动机:Maven 默认的中央仓库(Repo1)在国外,国内访问速度较慢。
方案:RVP 5.x 默认配置了华为云的 Maven 镜像仓库(mirrors.huaweicloud.com),用于下载所有依赖(repositories)和 Maven 插件(pluginRepositories),这极大地 加快了 国内用户的依赖下载和项目构建速度。


22.5. [核心] ruoyi-common-bom 详解

22.5.1. BOM (Bill of Materials) 设计模式解析

在 Maven 中,BOM (Bill of Materials) 即“物料清单”,是一种专门用于依赖版本管理的 pom.xml 文件。

它的核心设计哲学是

  1. 它不被继承:BOM 文件不作为其他模块的 <parent>
  2. 它只被导入:它的唯一目的是在根 POM 的 <dependencyManagement> 中被 <scope>import</scope> 导入。
  3. 它的职责单一:BOM 文件本身几乎只包含一个 <dependencyManagement> 块,这个块的作用就是“声明”一整套相互兼容的依赖版本。

RVP 5.x 架构就是利用此模式,将所有 ruoyi-common-* 插件化模块的版本,集中到 ruoyi-common-bom 中进行统一声明。

22.5.2. ruoyi-common-bom/pom.xml 源码解析:它“管理”了哪些版本?

我们来分析``RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-bom/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
<project ...>
<modelVersion>4.0.0</modelVersion>

<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-bom</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-doc</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-redis</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-satoken</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

关键点分析

  1. <packaging>pom</packaging>:它本身是一个 POM,不产生 jar 包。
  2. <dependencyManagement>:这是它的 唯一 核心内容。
  3. 声明内容:它声明了 RVP 框架所有“通用功能插件”(如 -core, -doc, -excel, -log, -redis, -mybatis, -satoken, -web 等)的版本。
  4. 版本统一:所有这些插件模块的版本,全部统一为 ${revision}

RVP 5.x 架构的依赖传递链
子模块 (ruoyi-demo) -> 根 POM (ruoyi-vue-plus) -> BOM (ruoyi-common-bom)

ruoyi-demo 模块需要使用 redis 功能时,它只需引入:

1
2
3
4
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-redis</artifactId>
</dependency>

Maven 会自动向上(根 POM)查找版本 -> 根 POM 通过 <scope>import</scope> 指向 ruoyi-common-bom -> ruoyi-common-bom 声明了 ruoyi-common-redis 的版本是 ${revision} -> ${revision} 被解析为 5.5.1

22.5.3. [重点] 为什么 bom 文件没有 <parent>

我们注意到 ruoyi-common-bom/pom.xml没有 <parent> 标签,它不继承自 ruoyi-vue-plus 根 POM。

这是一个至关重要的解耦设计

  • 痛点:如果 ruoyi-common-bom 继承了根 POM (<parent>ruoyi-vue-plus</parent>),那么任何想使用 RVP 通用模块(如 ruoyi-common-redis)的 外部项目,在导入此 BOM 时,都会被迫同时继承 ruoyi-vue-plus 的所有配置。这造成了不必要的强耦合。
  • RVP 5.x 方案:通过移除 <parent>ruoyi-common-bom 变成了一个“独立、自包含”的物料清单。任何 Maven 项目(无论是不是 RVP 项目)都可以安全地 <scope>import</scope> 这个 BOM,来获取 RVP common 模块的官方版本,而不会污染自己的父 POM 继承链。

随之而来的问题:既然 ruoyi-common-bom 没有父 POM,它如何知道 ${revision} 变量的值(5.5.1)呢?

答案在 bom 文件的 <properties> 标签中:

1
2
3
<properties>
<revision>5.5.1</revision>
</properties>

ruoyi-common-bom 必须 在自己的 pom.xml重新定义 一次 <revision> 变量。这是为了确保它在“独立”的情况下,也能正确解析其 <dependencyManagement> 中所有模块的版本号。

22.5.4. ruoyi-common/pom.xml 的职责:聚合所有 common 插件

现在我们再来看 RuoYi-Vue-Plus/ruoyi-common/pom.xml 文件。它的作用又是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<parent>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<modules>
<module>ruoyi-common-bom</module>
<module>ruoyi-common-social</module>
<module>ruoyi-common-core</module>
<module>ruoyi-common-doc</module>
</modules>

<artifactId>ruoyi-common</artifactId>
<packaging>pom</packaging>

ruoyi-common 模块的职责是“组织聚合”。它同样是一个 <packaging>pom</packaging> 模块,它不管理版本(那是 bom 的事),它只负责在 <modules> 标签中,将 所有 ruoyi-common-* 插件(包括 -bom 自己)聚合在一起。

最终结构

  1. pom.xml:通过 <module>ruoyi-common</module> 聚合 common 目录。
  2. ruoyi-common/pom.xml:通过 <module>ruoyi-common-core</module> 等标签,聚合所有插件子模块。
  3. ruoyi-common-bom/pom.xml:通过 <dependencyManagement>声明 所有插件子模块的 版本

这种设计实现了“组织结构”与“版本声明”的彻底分离。


22.6. ruoyi-modulesruoyi-demo 依赖分析

在 22.5 节中,我们解析了 ruoyi-common 模块如何通过 bom 文件来声明“通用插件”的版本。现在,我们进入“业务层”——ruoyi-modules 目录,分析业务模块(特别是 ruoyi-demo)是如何“消费”这些通用插件的。

22.6.1. ruoyi-modules/pom.xml:业务模块的“聚合器”

我们首先分析 RuoYi-Vue-Plus/ruoyi-modules/pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<parent>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<modules>
<module>ruoyi-demo</module>
<module>ruoyi-generator</module>
<module>ruoyi-job</module>
<module>ruoyi-system</module>
</modules>

<artifactId>ruoyi-modules</artifactId>
<packaging>pom</packaging>
  • <parent>:它继承自根 pom.xml (ruoyi-vue-plus),因此它能“感知”到根 POM 中定义的所有 <properties><dependencyManagement> 规则。
  • <packaging>pom</packaging>:它的身份同样是“父项目”。
  • <modules>:它的职责非常单一,就是作为“业务模块的聚合器”,将 ruoyi-demo(演示)、ruoyi-generator(代码生成)、ruoyi-job(调度任务)、ruoyi-system(系统核心)这四个业务模块组织在一起。

22.6.2. ruoyi-demo/pom.xml 依赖分析:按需引入 common 插件

ruoyi-demo 模块是 RVP 5.x 中用于“二次开发”的示例模块。分析它的 pom.xml,是理解 RVP 依赖管理“最佳实践”的关键。

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
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-modules</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>ruoyi-demo</artifactId>

<dependencies>

<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>

<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-redis</artifactId>
</dependency>

<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
</dependency>

</dependencies>

22.6.3. [重点] ruoyi-demo 如何实现“无需声明版本”?

我们注意到 ruoyi-demo<dependencies> 块中,所有 ruoyi-common-* 依赖 都没有声明 <version> 标签。这就是 RVP 5.x 架构“优雅”的体现。

其背后的依赖解析链条如下

  1. ruoyi-demo<dependencies> 中请求 ruoyi-common-redis(无版本)。
  2. Maven 沿着 demo<parent> 找到 ruoyi-modules (pom.xml)。
  3. ruoyi-modules 没有声明版本,Maven 继续沿着它的 <parent> 找到 pom.xml (ruoyi-vue-plus)。
  4. pom.xml<dependencyManagement> 块中,导入<scope>import</scope>)了 ruoyi-common-bom
  5. Maven 在 ruoyi-common-bom<dependencyManagement> 块中,找到了 ruoyi-common-redis 的版本声明:<version>${revision}</version>
  6. Maven 解析根 pom.xml<properties>,将 ${revision} 替换为 5.5.1
  7. 最终ruoyi-demo 成功引入了 ruoyi-common-redis:5.5.1

这种设计使得 ruoyi-demo(以及所有业务模块)在开发时,可以像“点餐”一样,按需引入 ruoyi-common-* 插件,而 完全不需要关心 这些插件的版本号,版本由根 POM 和 BOM 统一控制。

22.6.4. [实战] 二次开发:引入新依赖的最佳实践

在 RVP 基础上进行二次开发时,我们必然需要引入新的依赖。根据 RVP 5.x 的架构哲学,引入新依赖有严格的最佳实践。

场景一:引入 RVP common 模块(例如 demo 模块需要发邮件)

如果 ruoyi-demo 需要使用邮件功能,我们只需引入 ruoyi-common-mail 即可。

操作

  1. 打开 ruoyi-demo/pom.xml 文件。
  2. <dependencies> 块中,添加以下代码:
1
2
3
4
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mail</artifactId>
</dependency>

[最佳实践]严禁 添加 <version> 标签。ruoyi-common-bom 已经统一管理了所有 common 模块的版本。


场景二:引入全新第三方依赖(例如 commons-lang3

假设 ruoyi-demo 需要引入 org.apache.commons:commons-lang3 这个 RVP 默认未包含的依赖。

错误实践:直接在 ruoyi-demo/pom.xml 中添加 <dependency> 并硬编码 <version>3.12.0</version>
后果:这破坏了 RVP 的集中管理原则。如果 ruoyi-system 也引入了 3.13.0 版本的 commons-lang3,就会立刻产生版本冲突。

[最佳实践] 遵循 RVP 的“两阶段”模式:

步骤一:在根 pom.xml 中“声明”版本

  1. 打开 pom.xml (RuoYi-Vue-Plus/pom.xml)。
  2. <properties> 标签中,添加一个新的版本变量:
    1
    2
    3
    <properties>
    <commons-lang3.version>3.12.0</commons-lang3.version>
    </properties>
  3. <dependencyManagement> -> <dependencies> 标签中,添加依赖声明:
    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>${commons-lang3.version}</version>
    </dependency>

步骤二:在 ruoyi-demo/pom.xml 中“引入”依赖

  1. 打开 ruoyi-demo/pom.xml
  2. <dependencies> 块中,不带版本号 地引入:
    1
    2
    3
    4
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    </dependency>

结论:在二次开发中,所有新依赖(无论是 RVP 内部的还是第三方的),都应遵循“版本声明在根 POM (dependencyManagement),按需引入在子模块 (dependencies)”的黄金法则。


22.7. ruoyi-admin:唯一的“可执行”聚合器

我们现在分析 ruoyi-admin/pom.xml。这个模块是 RVP 5.x 架构的“主入口”,是最终运行的主服务。

22.6.1. ruoyi-admin/pom.xml 依赖解析:聚合所有 commonmodules

ruoyi-adminpom.xml 同样继承自根 POM,但它的职责不是声明,而是“实际引入”。

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
<parent>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId>
<version>${revision}</version>
</parent>
<packaging>jar</packaging>
<artifactId>ruoyi-admin</artifactId>

<dependencies>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-social</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-generator</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-demo</artifactId>
</dependency>
</dependencies>
  • <packaging>jar</packaging>:它的身份标识是 jar,意味着它将被打包成一个 jar 文件。
  • <dependencies>:它像一个“总装车间”,将 ruoyi-common-*(通用插件)和 ruoyi-modules-*(业务模块)实际引入 到项目中。
  • 版本管理:同样,所有依赖都 无需声明 <version>,版本全部由根 POM 的 <dependencyManagement>ruoyi-common-bom 统一控制。

22.7.2. [核心] spring-boot-maven-plugin:可执行“Fat Jar”的构建者

ruoyi-admin/pom.xml 中最关键的配置在 <build> 标签内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

spring-boot-maven-plugin 是 Spring Boot 官方提供的打包插件。它的存在,是 ruoyi-admin 能够被独立运行的关键。

22.7.3. [重点] 编译 (compile) vs 打包 (repackage) 的本质区别

1Maven 的标准 package(编译)和 Spring Boot 的 repackage(重新打包)会产生两种截然不同的 jar 包。

  1. 标准 package (编译产物)

    • 执行 Maven 的 package 命令,会先触发 compile 编译。
    • 此时 maven-jar-plugin 会创建一个 jar 包(在 target 目录中通常叫 ruoyi-admin.jar.original)。
    • 这个 jar只包含 ruoyi-admin 模块自己的 .class 文件和 resources
    • 它不包含 ruoyi-common-redissa-tokenspring-boot 等任何第三方依赖的 jar 包。
    • 结论:这个 original 包体积很小(几 MB),无法通过 java -jar 独立运行,因为它缺少依赖。
  2. repackage (Spring Boot 打包)

    • spring-boot-maven-plugin 插件的 <goal>repackage</goal> 会在标准 package 之后 执行。
    • 它会拿 original 包作为基础,然后将 所有<dependencies> 中引入的依赖(如 ruoyi-common-redis.jar, sa-token.jar 等)全部解压并重新打包 进最终的 jar 包中。

22.6.4. repackage 如何将 libclasses 打包?

repackage 插件会生成一个可执行的“Fat Jar”(在 target 目录中叫 ruoyi-admin.jar),其内部结构通常如下:

  • BOOT-INF/classes/:存放 ruoyi-admin 模块自身的业务代码 .class 文件。
  • BOOT-INF/lib/[核心] 存放所有第三方依赖的 jar 包(如 sa-token.jar)。
  • org/springframework/boot/loader/:存放 Spring Boot 的“启动加载器”,java -jar 命令真正执行的是这个加载器,它知道如何去加载 BOOT-INF 目录下的类和 lib

结论ruoyi-admin 通过聚合所有业务模块,并使用 spring-boot-maven-plugin,最终将自己打包成一个包含所有依赖、可独立运行的“Fat Jar”。


22.8. ruoyi-extend:独立运行的“卫星服务”

现在我们分析 ruoyi 的拓展模块 RuoYi-Vue-Plus/ruoyi-extend/pom.xml

22.7.1. ruoyi-extend/pom.xml 源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
<parent>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-extend</artifactId>
<packaging>pom</packaging>

<modules>
<module>ruoyi-monitor-admin</module>
<module>ruoyi-snailjob-server</module>
</modules>

ruoyi-modules 一样,ruoyi-extend 也是一个 <packaging>pom</packaging> 的聚合器:

  • 它继承自根 POM (ruoyi-vue-plus),获取依赖管理能力。
  • 它聚合了 ruoyi-monitor-admin(监控中心)和 ruoyi-snailjob-server(调度中心)两个模块。

22.7.2. extendmodules 的本质区别

ruoyi-modules(业务模块)和 ruoyi-extend(扩展模块)虽然都是聚合器,但它们的 设计目标 截然不同:

  • ruoyi-modules (如 ruoyi-system):聚合的是 业务库 (Library)。它们自身不能运行,必须被 ruoyi-admin 引入 并打包后,才能作为 admin 服务的一部分运行。
  • ruoyi-extend (如 ruoyi-monitor-admin):聚合的是 独立服务 (Application)。它们 不被 ruoyi-admin 引入。相反,它们和 ruoyi-admin 是“兄弟”关系,都是可以独立运行的 Spring Boot 应用。

22.7.3. ruoyi-snailjob-server/pom.xml 源码解析

ruoyi-snailjob-server 是 SnailJob 的调度服务端。分析其 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
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-extend</artifactId>
</parent>
<packaging>jar</packaging>
<artifactId>ruoyi-snailjob-server</artifactId>

<dependencies>
<dependency>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-server-starter</artifactId>
<version>${snailjob.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

关键点分析

  1. <packaging>jar</packaging>:它也是一个 jar 包项目。
  2. snail-job-server-starter:它引入了 SnailJob 的 服务端 依赖。
  3. spring-boot-maven-plugin:它 同样<build> 块中配置了 repackage 目标,证实了它也会被打包成一个可独立运行的“Fat Jar”。

22.7.4. 总结:RVP 5.x 的三个可执行服务

通过对 ruoyi-adminruoyi-monitor-adminruoyi-snailjob-server 三个模块 pom.xml 文件的推断,我们得出结论:

RVP 5.x 架构在 Maven 层面定义了三个 可独立执行的服务。它们都在各自的 <build> 插件中配置了 spring-boot-maven-plugin,用于生成可运行的“Fat Jar”。

模块是否可打包最终产物 (Fat Jar)职责
ruoyi-adminruoyi-admin.jarRVP 主业务服务
ruoyi-monitor-adminruoyi-monitor-admin.jarSpring Boot Admin 监控服务端
ruoyi-snailjob-serverruoyi-snailjob-server.jarSnailJob 任务调度服务端
ruoyi-system(无,被 admin 聚合)业务库 (Library)
ruoyi-common-redis(无,被 admin 聚合)通用插件库 (Library)

这就是 RVP 5.x 架构下,我们为什么需要启动三个 Java 服务(admin, monitor, snailjob)的 Maven 根本原因。