ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Maven 3.9.6升级实战:配置优化、踩坑记录与插件兼容指南

Maven 3.9.6升级实战:配置优化、踩坑记录与插件兼容指南 作为一个常年跟 Java 项目、CI 流水线、私有仓库打交道的人我对 Maven 的感情一直很复杂。一方面它稳定、可靠是 Java 生态的基石之一另一方面它偶尔冒出来的诡异报错也实打实地让人头疼。这次把环境从 3.6.3 和 3.8.x 统一升到 Apache Maven 3.9.6前前后后折腾了一周踩了配置的坑也顺手解决了好几个历史遗留的插件兼容问题。这篇文章不聊虚的把我升级过程中的实测结果、配置细节、踩坑实录全部摊开讲清楚。先说结论Maven 3.9.6 不是一个颠覆性的版本但它更像是 Apache Maven 团队在 3.9.x 这条线上的“稳定交付物”。如果你还在用 3.6.x 或者更老的版本又恰好遇到资源过滤、插件版本警告、构建输出混乱这类问题升级到 3.9.6 往往能让你少掉很多头发。这篇文章适合所有用 Maven 的 Java 开发者尤其是从老版本升级、切换环境、或者刚接触 Maven 想在 IDEA 里正确配置的人。1. 3.9.6 到底改了些什么升级前你得知道的版本逻辑很多同学看到版本号 3.9.6下意识会觉得这就是一个“补丁版本”改点 bug 就完事了。这个理解不算错但在实际迁移的时候你会发现它背后有一整套版本策略的变化搞不清楚这些升级后配置容易踩坑。1.1 3.9.x 和 3.8.x 是什么关系Maven 4 已经规划了很久但一直没正式铺开所以 3.9.x 实际上是 Apache Maven 在 3.x 主干上的延续版本。它和 3.8.x 的关系不是简单的“Bugfix”而是包含了多项面向未来 Maven 4 的铺垫性改动尤其是构建日志、依赖解析机制、插件版本默认值、仓库元数据缓存策略这几个方向。3.9.6 发布在 2024 年初属于 3.9.x 系列里比较成熟的版本。它的 JDK 最低要求依旧是 JDK 8但我实测在 JDK 17 和 JDK 21 环境下跑 3.9.6稳定性明显比 3.6.3 好尤其是编译插件、surefire 测试插件、resources 插件这些基础组件的默认版本都被拉到了对 JDK 17 更友好的版本上。这一点对现在主流的新项目来说非常关键。1.2 升级后体感最明显的三个变化第一构建日志的格式变“整齐”了。3.9.6 对 Maven 自身的日志输出做了大量清理很多内部调试信息不再默认打印同时也屏蔽了一堆老的插件警告。如果你之前被那种“一行构建信息夹着五六个 WARNING”的输出搞到崩溃升到 3.9.6 会舒服很多。第二依赖解析时的元数据缓存策略变了。Maven 3.9.x 对远程仓库的 metadata 缓存、快照版本的更新时间判断做了调整。简单说就是它不会像老版本那样频繁地去远程仓库核对 SNAPSHOT 版本所以构建速度会快一些。但你如果习惯了老版本那种“每次构建都尝试拉最新快照”的行为升级后可能会发现某个快照更新不生效非得加-U参数强制刷新。这个我后面会专门讲。第三对 Maven Wrapper 的支持更顺畅。3.9.6 的mvnw脚本兼容性更好在 Windows、Linux、macOS 上跑起来基本不会出现奇怪的换行符或者权限问题。你要是维护着多个 Java 项目强烈建议把项目的 Maven Wrapper 也升级到 3.9.6这样团队协作时大家的构建版本保持一致能少掉巨多“我本地能编但 CI 上挂了”的扯皮。1.3 我的升级建议如果你现在的项目是纯 Java 8、用的插件版本也比较老、不依赖新的构建特性那升不升 3.9.6 其实影响不大因为 Maven 3.x 的 API 兼容性一直做得不错。但如果你的项目是 Java 11 以上、用到了 Spring Boot、多模块聚合、或者你频繁被maven-resources-plugin、maven-compiler-plugin的兼容性问题恶心到那我建议你直接升。升级的时候有一点要特别注意Maven 3.9.6 对某些插件的默认版本进行了提升但这些默认版本不一定和项目里已经显式声明的插件版本兼容。如果项目 POM 里已经写死了某个很老的插件版本那升级后首先要关注的就是这些插件能不能正常跑起来。2. 下载、安装与环境变量配置从零装好 Maven 3.9.6这一节写给刚接触 Maven 的同学也写给那些要在一台新机器上部署 Maven 的老手。因为 3.9.6 的安装过程中有一个非常容易忽略的点JDK 版本和 settings.xml 的归属关系。2.1 下载渠道与版本选择Maven 的下载官网是maven.apache.org进入 Download 页面后直接选apache-maven-3.9.6-bin.tar.gz或者apache-maven-3.9.6-bin.zip。Windows 用户下载 zipLinux 和 macOS 用户下载 tar.gz。这里有个很实用的经验如果你需要下载历史版本比如公司强制要求某个老版本不要只在当前官网翻。Apache 的归档地址是archive.apache.org/dist/maven/maven-3/里面保留了 3.0 到 3.9.x 的所有版本连二进制包和源码包都完整。热搜词里提到的“apache 官网下载 maven 3.6.x版本”就是从这个归档地址里找。很多公司内网环境无法访问外网下载归档地址反而比官网主页更好用。2.2 环境变量配置Windows、Linux、macOS 三平台实操安装包解压后建议先把目录名改成maven-3.9.6这样的形式放到/usr/local或D:/dev/这类规范的软件目录里方便后续维护。Windows 下的配置流程是右键“此电脑”进入“属性”打开“高级系统设置”点击“环境变量”。新建系统变量MAVEN_HOME值设置为 Maven 解压目录比如D:\dev\maven-3.9.6。在系统变量Path中新增%MAVEN_HOME%\bin。打开新的命令行窗口执行mvn -version验证。macOS 和 Linux 下我习惯把配置写到~/.zshrc或~/.bashrc里export MAVEN_HOME/usr/local/maven-3.9.6 export PATH$MAVEN_HOME/bin:$PATH执行source ~/.zshrc让配置生效然后mvn -version。验证时请重点关注第一行的输出它会同时显示 Maven 版本和当前使用的 Java 版本。如果你看到的是Error: JAVA_HOME is not set correctly说明 JDK 的环境变量没弄好。Maven 3.9.6 需要 JDK 8 及以上我建议开发环境统一用 JDK 17 或 JDK 21兼容性和性能都更稳妥。2.3 settings.xml 三件套本地仓库路径、镜像、ProfileMaven 的配置文件有两个层级全局配置在$MAVEN_HOME/conf/settings.xml用户级配置在~/.m2/settings.xml。两个文件都存在时用户级的配置会覆盖全局配置。这里分享一个我自己坚持的规范全局配置只保留最基础的安全设置比如默认镜像、本地仓库路径、代理信息用户级配置放属于个人或项目的 profile比如某个私服账号、仓库认证信息。这样多台机器同步配置时不容易把敏感信息带去公司之外的机器。本地仓库路径建议从默认的~/.m2/repository改到独立目录比如/data/maven-repo或D:/maven-repo。原因很简单当仓库膨胀到几十 GB 时系统盘空间会非常吃紧而且重装系统、切换 SSD 时迁移起来也麻烦。用软链接或者直接在 settings.xml 里指定路径都能解决这个问题。settings localRepository/data/maven-repo/localRepository /settings2.4 阿里云镜像配置细节与 mirrorOf 的坑国内网络环境下不配镜像基本没法愉快地拉依赖。热搜词里“maven配置阿里云仓库”“maven配置多个镜像仓库”这两个词条对应的就是下面这段操作。主流的镜像配置是在settings.xml里新增一个 mirrormirrors mirror idaliyun/id nameAliyun Maven Mirror/name urlhttps://maven.aliyun.com/repository/public/url mirrorOfcentral/mirrorOf /mirror /mirrors这里的mirrorOf是关键。central表示只把 Maven 中央仓库的请求转发到阿里云私服或者其他自定义仓库不受影响。如果你为了省事写成mirrorOf*/mirrorOf那么所有远程仓库请求都会被转发到阿里云这在只依赖中央仓库的场景下没问题但一旦你接入了公司私有 Nexus 或 Artifactory依赖就会拉不下来或拉到错误快照。我见过不少人在这个坑里栽跟头所以要特别提醒配置多镜像时最好给每个镜像写清楚mirrorOf的范围不要图省事用通配符。如果你要配置多个仓库另一个思路是使用 profile 里的 repository而不是镜像profiles profile idmy-repos/id repositories repository idaliyun/id urlhttps://maven.aliyun.com/repository/public/url /repository repository idcompany-nexus/id urlhttps://nexus.company.com/repository/maven-public//url /repository /repositories /profile /profiles这种方式更适合多私有仓库并行存取的场景激活 profile 后Maven 会按顺序依次尝试每个仓库解析依赖直到找到目标构件。3. IDEA 里集成 3.9.6最容易翻车的四个设置IDEA 对 Maven 的集成已经非常成熟但正因为 IDE 替我们做了太多事情导致很多人根本不了解自己项目到底用的哪个 Maven 版本、哪份 settings.xml。这里把最容易翻车的几个点列一下。3.1 解决“IDEA 默认 Maven 版本”问题IDEA 新版本往往自带一个 Maven版本可能不是 3.9.6。这就导致一种很尴尬的情况命令行里mvn -version显示 3.9.6IDEA 的 Maven 面板里却是自带的 3.8.x 或 3.6.x构建行为完全不一致。正确的做法是在 IDEA 里手动指定 Maven 路径打开File - Settings - Build, Execution, Deployment - Build Tools - Maven。在Maven home path那里点右侧的文件夹图标选择你的apache-maven-3.9.6解压目录。下方的User settings file和Local repository会自动读取对应路径确认指向你的settings.xml和仓库目录。改完后建议点一下 Maven 侧边栏里的刷新按钮两个循环箭头的图标让项目重新导入。3.2 Maven Runner 参数与 JDK 设置另一个很容易被忽略的地方是Runner选项卡。在Maven设置页的最下面有一个Runner子页面里面有JRE和VM Options两个字段。JRE字段决定了 IDEA 里跑 Maven 命令时用的 Java 版本。如果你的项目要求 JDK 17但这里选的是 JDK 8那么编译大概率失败。正确做法是选择项目实际使用的 JDK 版本并且和Project Structure - Project SDK保持一致。VM Options里可以设置一些 Maven 运行时的 JVM 参数比如-Xmx2048m -Dfile.encodingUTF-8大型多模块项目构建时默认的 512MB 堆内存经常不够经常导致 OOM 或者 GC 频繁。保守起见设置 2GB 以上会稳很多。3.3 多模块项目在 IDEA 中的注意点多模块项目导入 IDEA 后父 POM 会显示为一个普通的 Maven 模块所有子模块会聚合在父模块下面。很多新人会犯的错是单独对某个子模块执行clean install结果发现依赖的子模块代码没更新一直用的是本地仓库里的旧版本。这是因为子模块之间的依赖Maven 默认也是从本地仓库解析的不是自动用 IDEA 的模块依赖。解决办法有两个在 IDEA 的 Maven 面板里对父工程执行clean install一次性把整个聚合工程的所有模块都构建一遍。开发阶段用mvn -pl 子模块名 -am install-am参数会同时构建该子模块依赖的其他本地模块。IDEA 里执行这些命令前先确保 Maven 面板中父工程下的所有模块都已经正常加载否则很可能出现 Module not found 之类的导入错误。4. 打包、私服与生命周期绕不开的几个进阶点这个标题看起来很大但实际问得最多的就几个点clean install和package有什么区别、jar/war/pom 三种打包方式怎么选、Spring Boot 项目的 repackage 是什么、依赖冲突怎么排查。4.1 生命周期到底在做什么Maven 的构建生命周期是三个clean、default、site。日常用得最多的是default生命周期它包含多个阶段按顺序依次执行validate、compile、test、package、verify、install、deploy。mvn clean单独执行时只清理 target 目录。mvn package会执行到 package 阶段也就是编译、跑测试、打 jar/war 包。mvn install则在 package 基础上把产物安装到本地仓库。mvn deploy则会把包上传到远程私服。很多人问我用了mvn package打出来的包为什么不能直接跑这通常是因为没有执行clean导致 target 目录里有上次构建的残留文件。所以实际项目操作中mvn clean package或者mvn clean install才是更严谨的姿势热搜词里“maven命令行 clean install”说的就是这个。4.2 jar/war/pom 三种打包方式与 Spring Boot repackage在packaging标签里最常见的三种值分别是jar、war、pom。纯工具库和普通应用后端服务用jar传统 Web 应用需要部署到外部 Tomcat 时用war多模块聚合的父 POM 用pom它本身不产出可执行文件只用来统一管理依赖版本和模块清单。Spring Boot 项目比较特殊。它的默认打包方式是jar但打出来的 jar 和普通 Java 库不一样是 Spring Boot 的可执行 fat jar里面包含了所有依赖和内嵌 Tomcat。实现靠的是spring-boot-maven-plugin的repackagegoal它在 package 阶段后把原始 jar 重新加工一遍。如果你在 IDEA 里对 Spring Boot 项目执行mvn package然后发现 jar 只有几十 KB根本跑不起来多半是spring-boot-maven-plugin没有配置executions绑定到repackage。正确配置是build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId executions execution goals goalrepackage/goal /goals /execution /executions /plugin /plugins /build4.3 依赖作用域与依赖冲突排查Maven 的依赖常见作用域有compile、provided、runtime、test、system、import。最容易被忽略的是provided比如 Servlet API、Lombok 这类只在编译期需要、运行期由容器或编译处理器提供的依赖用provided能避免打进最终产物里。依赖冲突是 Maven 项目永恒的痛。冲突的根源是 Maven 的“最近优先、先声明优先”解析原则。当两个不同的依赖树引入同一个框架的不同版本时Maven 默认选择路径最短的那个而不是版本最新的那个。这会导致各种奇怪的NoSuchMethodError、ClassNotFoundException。排查依赖冲突最常用的命令是mvn dependency:tree它会打印出完整的依赖树你可以清楚地看到每个依赖是从哪个传递链引入的。比如看到某个 jar 同时存在 2.5.6 和 3.1.0 两个版本就可以在 pom 里用exclusion排除冲突项或者用dependencyManagement统一强制版本。dependency groupIdcom.example/groupId artifactIdexample-lib/artifactId version1.0.0/version exclusions exclusion groupIdcommons-logging/groupId artifactIdcommons-logging/artifactId /exclusion /exclusions /dependency4.4 部署到私服的配置项目需要发布到公司私服时光有deploy命令还不够得在 pom 里配置distributionManagementdistributionManagement repository idcompany-releases/id urlhttps://nexus.company.com/repository/maven-releases//url /repository snapshotRepository idcompany-snapshots/id urlhttps://nexus.company.com/repository/maven-snapshots//url /snapshotRepository /distributionManagement私服的账号密码要配置在settings.xml的servers里ID 必须和 pom 里的id一致servers server idcompany-releases/id usernamedeploy-user/username passworddeploy-password/password /server server idcompany-snapshots/id usernamedeploy-user/username passworddeploy-password/password /server /servers之所以放在settings.xml而不是 pom 里是因为distributionManagement在 pom 里是开源可见的所有人都能看到仓库地址但账号密码属于敏感信息放在用户级配置文件里就可以做到“只在本机生效不进 Git 仓库”。5. 升级 3.9.6 之后我踩过的坑报错排查实录这一节全是真实经历。升级到 3.9.6 后我的第一反应是“版本号变了项目应该无感才对”。事实证明升级带来的插件兼容性问题比预想中多但每个问题都能追溯到一个明确的原因。5.1 NoClassDefFoundError: org/apache/maven/shared/filtering/MavenFilteringException这个报错在我升级后最先蹦出来热搜词里也有人在搜说明它不算罕见。看报错信息是在执行mvn clean package时使用了maven-resources-plugin做资源过滤结果类加载器找不到MavenFilteringException这个类。根因不是 Maven 3.9.6 本身而是项目里的maven-resources-plugin版本太老。老版本的maven-resources-plugin依赖的maven-filtering库版本也老而 Maven 3.9.6 的类加载机制对这种老库的兼容性不如 3.6.x。解决办法有两种一是在 pom 里显式升级maven-resources-plugin到 3.3.1 或更高版本二是在项目的properties里声明maven-filtering的版本让插件使用新版依赖properties maven-resources-plugin.version3.3.1/maven-resources-plugin.version /properties我最后选择了升级插件版本因为这样更干净也避免后续再出现其他 shared 组件兼容性问题。5.2 mvn validate 失败validate阶段本身做的事情很少在大多数项目里它不应该失败。当你在 3.9.6 下遇到mvn validate失败常见原因有三类父 POM 解析失败、settings.xml 配置错误、本地仓库元数据损坏。其中“本地仓库元数据损坏”是最难排查的。Maven 在下载依赖时会把.lastUpdated文件和部分下载失败的临时文件留在本地仓库里。当这些文件损坏后Maven 解析依赖时会直接判定“该依赖不可用”反复构建都失败但原因又不在当前项目里。遇到这种情况先检查本地仓库里对应目录下的_remote.repositories和.lastUpdated后缀文件把可疑的一概删除然后重新执行mvn -U clean validate。如果问题依旧再看 settings.xml 里是否误配了offlinetrue/offline这个选项会强制 Maven 不联网只解析本地仓库已有内容。5.3 快照版本不更新这是一类非常容易让人抓狂的问题。项目里依赖了某个内部模块的-SNAPSHOT版本在 3.6.3 下构建时Maven 每次都会去远程仓库检查一下快照是否更新。升级到 3.9.6 后有时同一个版本的快照在远程仓库变了本地却一直用旧包构建结果时好时坏。原理是 Maven 3.9.x 调整了repository system对快照元数据的缓存时间。默认情况下Maven 不会在每次构建时都去远程仓库核对快照更新时间而是使用本地缓存的远程元数据。这个改动是为了提升构建速度但会让很多开发者误以为“升级后拉包有问题”。解决办法是在执行构建时加上-U参数强制刷新快照mvn clean install -U如果你希望某个项目每次都检查快照也可以在 pom 里的repository配置中加snapshotsupdatePolicyalways/updatePolicy/snapshots。5.4 日常排查工具与思路除了上面几个具体报错我还想分享几个 Maven 日常排查思路因为很多问题都不是“一条命令”能解决的需要组合工具逐步定位。先看mvn -X的调试信息。-X参数会输出 Maven 执行过程中的全量调试日志包括每个依赖是从哪个仓库下载的、哪些 jar 被跳过、哪些插件的哪个 goal 在哪个阶段执行。虽然信息量大但报错时它往往能直接告诉你定位方向。其次用mvn help:effective-pom查看最终的生效 POM。这个命令会把当前项目的 POM、父 POM、profile 合并后的完整版本打印出来。有时候配置看起来没问题但某个 pluginManagement 或 dependencyManagement 在父 POM 里已经写死了老版本用这个命令一眼就能发现。最后是mvn dependency:tree和mvn dependency:analyze的组合。前者解决“依赖从哪来”的问题后者解决“哪些依赖没用到、哪些用了但没声明”的问题。定期执行这两个命令能有效减少依赖冲突和隐式依赖带来的潜在风险。还有一个经验之谈如果你在 IDEA 里反复执行mvn clean install但本地仓库的 jar 包内容一直不对可以关掉 IDEA 的 Maven 自动导入手动执行一次命令行构建再用 IDEA 的Reload All Maven Projects重新导入。这一步看着粗暴却能解决 IDEA 的 Maven 缓存和本地仓库脱节的许多奇怪问题。我在实际项目里把开发机、CI 服务器、测试服务器全部统一到 Maven 3.9.6 之后最大的感受是“安静了”。构建日志清爽依赖解析稳定插件的默认版本也跟上了 Java 17 的节奏。如果你正在老版本和日常报错之间反复挣扎与其继续打补丁不如直接花一个下午把版本升到 3.9.6。最后再分享一个小技巧升级完别急着删旧版本先用mvn help:effective-settings对比一下新旧两个版本的配置文件差异确认没有遗漏的私有仓库和认证信息再清理旧版。这样切换环境的动作会更平滑也不容易在第二天上班时被同事一个“仓库拉不下来”的问题打断。
返回列表