Maven父子模块版本同步:告别手动修改的痛点解法
做Maven多模块开发的同学,大概率会遇到一个头疼问题:父子模块有继承关系时,子模块的<parent>标签中,父模块版本是写死的。一旦父模块版本升级,所有子模块都要手动修改继承的版本号,不仅繁琐,还容易漏改、错改,效率极低。
更坑的是,很多人会尝试用自定义属性(比如${framework.version})来管理父模块版本,像这样配置子模块:
<parent> <groupId>com.aurora</groupId> <artifactId>aurora-framework</artifactId> <version>${framework.version}</version> </parent>结果直接报错「Properties in parent definition are prohibited」,这背后的核心根源的是Maven的解析顺序:Maven会先解析<parent>标签确定继承关系,此时<properties>里的自定义变量还未加载,自然无法识别,这也是父子模块版本同步的核心痛点。
今天就分享一个官方推荐的最优解法,基于Maven 3.5.0+支持的CI Friendly Versions(${revision}变量),搭配flatten插件,一次配置,终身受益,既解决手动改版本的繁琐,又避免外部引用“找不到包”的问题。
核心解法:${revision}变量+flatten插件,双重保障
Maven官方专门为多模块版本同步痛点,开放了3个特殊变量:${revision}、${changelist}、${sha1},其中最常用的就是${revision}——它可直接在<parent>标签中使用,无需担心解析问题,完美解决自定义变量报错的问题。
但仅用${revision}还不够:如果缺少flatten插件,install到仓库的pom文件会仍带有${revision}变量,外部项目(如gateway)无法解析,会出现“找不到包”“版本不存在”的报错。因此,flatten插件是不可或缺的补充,其核心作用是将仓库中的pom文件“扁平化”,把${revision}替换为真实版本,确保外部项目能正常解析依赖。
具体操作步骤(一步到位,直接复制可用)
步骤1:修改父模块pom.xml(核心配置,含flatten插件)
将父模块的版本改为${revision},在<properties>中定义该变量,同时配置flatten插件(解决找不到包问题),改这一处即可实现全局版本统一管理:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.aurora</groupId> <artifactId>aurora-framework</artifactId> <version>${revision}</version> <!-- 用revision变量替代写死版本 --> <packaging>pom</packaging> <properties> <revision>1.0.0-SNAPSHOT</revision> <!-- 统一版本管理,升级时改这里 --> </properties> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>flatten-maven-plugin</artifactId> <version>1.5.0</version> <inherited>true</inherited> <!-- 子模块继承插件,无需单独配置 --> <configuration> <updatePomFile>true</updatePomFile> <!-- 强制更新仓库中的pom文件 --> <flattenMode>resolveCiFriendliesOnly</flattenMode> <!-- 仅替换${revision}等CI友好变量 --> </configuration> <executions> <execution> <id>flatten-pom</id> <!-- 唯一id,避免重复报错 --> <phase>process-resources</phase> <goals> <goal>flatten</goal> <!-- 执行变量替换,生成扁平化pom --> </goals> </execution> <execution> <id>flatten-clean</id> <!-- 唯一id,与上一个不重复 --> <phase>clean</phase> <goals> <goal>clean</goal> <!-- 清理旧的扁平化pom,避免缓存干扰 --> </goals> </execution> </executions> </plugin> </plugins> </build> <!-- 其他配置(如dependencyManagement等) --> </project>插件配置关键说明(缺一不可,避免找不到包):
插件必须放在<build><plugins>下,而非<pluginManagement>(后者仅定义不执行,无法生成扁平化pom);
两个execution配置需完整,id唯一,确保插件正常执行替换和清理操作;
<inherited>true</inherited>可让子模块自动继承插件,无需单独配置。
步骤2:修改所有子模块pom.xml
子模块的<parent>标签中,直接使用${revision}引用父模块版本,无需自定义变量,也无需额外配置插件(继承父模块插件):
<parent> <groupId>com.aurora</groupId> <artifactId>aurora-framework</artifactId> <version>${revision}</version> <!-- 直接使用官方变量,避免报错 --> </parent> <!-- 子模块自身版本可省略,或同样使用${revision},保持一致 -->
<artifactId>aurora-module-demo</artifactId>进阶技巧:版本升级更高效
后续父模块版本需要升级时,只需做一件事:修改父模块<properties>中的<revision>值,所有子模块会自动跟随父模块版本,无需逐个修改。
如果是CI/CD流水线部署,还可以不用在pom.xml中写死<revision>,通过命令行动态注入版本,更灵活:
# 部署正式版,动态指定版本为2.0.0-RELEASE mvn clean install -Drevision=2.0.0-RELEASE常见坑点:最易踩雷!源码pom与仓库pom的区别
配置完插件、执行install后,很多同学会陷入一个误区:打开IDE里的源码pom.xml,发现里面的${revision}依然存在,就以为插件没生效——这其实是完全正常的!
核心关键点:flatten插件不会修改你的源码pom文件。它的作用是在执行mvn clean install时,生成一个“扁平化”的pom文件(替换掉所有${revision}为真实版本),并将这个修改后的pom文件安装到本地仓库(或部署到远程仓库);而你本地IDE里看到的源码pom,依然会保留${revision},方便后续版本统一修改。
举个实际场景(对应aurora-framework-starter模块):
1. 源码pom(IDE中可见):依赖版本依然是${revision},无需修改,正常现象:
<dependency> <groupId>com.aurora</groupId> <artifactId>framework-web</artifactId> <version>${revision}</version> </dependency>2. 仓库pom(本地.m2仓库中可见):插件会自动将${revision}替换为真实版本(如1.0.0-SNAPSHOT),这才是外部项目(如gateway)实际依赖时读取的pom文件:
<dependency> <groupId>com.aurora</groupId> <artifactId>framework-web</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency>避免踩坑的3个关键操作:
执行install时,必须加上clean(mvn clean install),确保清理旧的未扁平化的pom文件;
验证插件是否生效,不要看IDE源码,要去本地仓库(路径示例:C:\Users\xxx\.m2\repository\com\aurora\aurora-framework-starter\1.0.0-SNAPSHOT\)查看生成的pom;
父pom中flatten插件的flattenMode必须设为resolveCiFriendliesOnly,避免替换失败。
问题排查:仍找不到包?看这2点
如果配置完插件、执行mvn clean install后,外部项目仍提示“找不到包”,可重点排查以下2个问题:
本地仓库存在旧的未扁平化pom:执行mvn clean install重新生成,覆盖旧文件;
插件版本不兼容:推荐使用1.3.0及以上版本,过低版本可能存在变量替换不彻底的问题。
总结
Maven父子模块版本同步的核心痛点,在于<parent>标签无法解析自定义属性,而官方${revision}变量完美解决了这一问题;搭配flatten插件,则能彻底解决外部项目“找不到包”的后续隐患。
整个方案只需两步核心配置:父模块配置${revision}变量+flatten插件,子模块直接引用变量,就能实现版本统一管理——升级时改一处生效所有子模块,既减少手动操作,又避免报错,是多模块开发的必备技巧。记住:不用自定义变量,认准${revision}+flatten插件,就能告别版本管理的烦恼~