简介Apache Maven 3.6.3 是面向 Java 开发者的项目构建与依赖管理工具适合需要标准化构建流程、统一管理第三方库的初中级工程师及团队使用。该版本在依赖解析、冲突处理与内存占用上做了优化构建速度与稳定性均有提升并延续了本地仓库、远程仓库、中央仓库三层依赖体系通过 pom.xml 集中声明依赖与插件配置。资源包共 2000 个文件以 sha1 校验文件、pom 描述文件、repositories 仓库配置、jar 依赖包为主另含少量 license、so、dll、cmd 等运行与脚本文件压缩包约 134.84MB完整保留了 Maven 发行版的目录结构。目前已有 194 人学习下载。借助这份资源读者可快速搭建本地 Maven 环境理解生命周期各阶段与插件扩展机制掌握依赖冲突排查与仓库配置思路为日常 Java 项目构建与持续集成打下基础。1. 为什么 2024 年还有人在用 Maven 3.6.3一个被低估的稳定版本打开任何一个稍具规模的 Java 项目pom.xml 几乎是标配。但真正让一线工程师头疼的往往不是写依赖而是构建工具本身的版本选择。Maven 3.6.3 这个版本号在 3.8.x、3.9.x 已经普及的今天依然出现在大量企业级项目的 CI 配置里。原因很直接它足够稳定插件生态兼容性经过多年验证而且不像 3.8 之后对 HTTP 仓库做了强制阻断很多老项目的私服地址还能直接跑通。这篇文章面向三类人接手了老项目、发现构建脚本锁死在 3.6.3 的维护者需要在离线或内网环境搭建构建链的工程师以及想搞清楚 Maven 版本差异到底影响哪些环节的技术负责人。我会从版本定位、安装配置、依赖解析机制、插件绑定、常见报错排查一路讲到进阶的构建优化技巧每一步都给出可复现的命令和参数说明。读完你应该能独立判断手里的项目到底该不该升级以及不升级的情况下怎么把 3.6.3 用到极致。2. Maven 3.6.3 的安装与基础环境搭建2.1 版本定位3.6.3 在 Maven 版本谱系里的位置Maven 的版本迭代大致可以分成几个阶段。3.5.x 解决了 JDK 9 的兼容问题3.6.x 系列重点优化了构建性能和依赖解析的并发能力而 3.8.x 开始引入了对 HTTP 仓库的默认阻断策略3.9.x 则进一步收紧了插件版本解析规则。3.6.3 是 3.6 分支的最后一个补丁版本发布于 2019 年底它修复了 3.6.2 中几个与并发下载相关的偶发问题同时没有引入后续版本那些会破坏老项目构建的强制策略。这意味着什么如果你的项目依赖的私服还是 HTTP 协议或者用了一些年久失修的插件3.6.3 往往是最后一个能“无痛跑通”的版本。常见做法是新项目直接用 3.9.x老项目维护继续锁 3.6.3两者通过 Maven Wrapper 隔离互不干扰。2.2 下载与目录结构把 Maven 装到该装的地方安装 Maven 本身不复杂但目录规划直接影响后续多版本共存。我一般会把不同版本的 Maven 放在同一个父目录下用版本号区分。# 假设下载的是 apache-maven-3.6.3-bin.tar.gz # 解压到统一的工具目录 mkdir -p /opt/maven tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /opt/maven # 目录结构确认 ls /opt/maven/apache-maven-3.6.3/ # bin/ conf/ lib/ boot/ LICENSE NOTICE README.txt解压后的目录里bin/放的是 mvn 可执行脚本conf/放的是全局配置文件 settings.xmllib/是 Maven 自身的依赖包。关键点在于不要急着配环境变量先确认 JDK 版本匹配。Maven 3.6.3 要求 JDK 8 及以上但如果你用的是 JDK 17需要额外注意一些老插件的反射调用问题。# 配置环境变量写入 ~/.bashrc 或 /etc/profile export MAVEN_HOME/opt/maven/apache-maven-3.6.3 export PATH$MAVEN_HOME/bin:$PATH # 验证 mvn -v # Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) # Maven home: /opt/maven/apache-maven-3.6.3 # Java version: 1.8.0_381, vendor: Oracle Corporationmvn -v的输出里Maven home 和 Java version 这两行必须确认无误。如果 Java version 显示的不是你预期的 JDK说明 JAVA_HOME 指向有问题Maven 会优先用 JAVA_HOME 而不是 PATH 里的 java。2.3 settings.xml 的关键配置镜像、本地仓库与代理settings.xml 是 Maven 行为控制的核心文件分全局$MAVEN_HOME/conf/settings.xml和用户级~/.m2/settings.xml两层。用户级配置会覆盖全局配置我一般只改用户级的方便多版本共享。!-- ~/.m2/settings.xml 关键片段 -- settings !-- 本地仓库位置默认是 ~/.m2/repository -- localRepository/data/maven-repo/localRepository mirrors !-- 内网镜像加速依赖下载 -- mirror idinternal-mirror/id mirrorOfcentral/mirrorOf urlhttp://nexus.internal.com/repository/maven-public//url /mirror /mirrors profiles profile idjdk8/id activation activeByDefaulttrue/activeByDefault jdk1.8/jdk /activation properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties /profile /profiles /settingslocalRepository改到数据盘是血泪经验默认的~/.m2/repository在系统盘依赖多了之后动辄几个 G重装系统或容器重建时全部丢失。mirrorOf写central表示只镜像中央仓库如果私服还代理了其他仓库可以写*或external:*。activeByDefault配合 jdk 激活条件能保证不同 JDK 环境下编译参数自动切换。注意3.6.3 对 HTTP 协议的镜像地址不做阻断但 3.8.x 之后会直接拒绝。如果你在 3.6.3 里配了 HTTP 私服升级 Maven 时这个配置必须同步改成 HTTPS 或加白名单。3. 依赖解析与冲突处理3.6.3 的解析逻辑拆解3.1 依赖调解机制最短路径优先与第一声明优先Maven 的依赖解析有两个核心规则最短路径优先以及路径相同时第一声明优先。理解这两条能解决 80% 的依赖冲突问题。假设项目直接依赖 AA 依赖 B路径长度 2同时项目直接依赖 CC 依赖 DD 依赖 B路径长度 3。那么最终生效的是 A 传递过来的 B因为路径更短。如果两条路径长度相同比如项目同时依赖 A 和 CA 和 C 都传递依赖了不同版本的 B那么谁在 pom.xml 里声明得更靠前谁传递的 B 就生效。!-- 示例两个路径长度相同的传递依赖 -- dependencies !-- 声明在前它传递的 common-lib 1.0 生效 -- dependency groupIdcom.example/groupId artifactIdmodule-a/artifactId version1.0.0/version /dependency !-- 声明在后它传递的 common-lib 2.0 被忽略 -- dependency groupIdcom.example/groupId artifactIdmodule-c/artifactId version1.0.0/version /dependency /dependencies这个规则在 3.6.3 和后续版本里没有变化但 3.6.3 的解析器在并发下载时对元数据的缓存策略略有不同偶尔会出现“同一份 pom 在不同机器上解析出不同结果”的玄学问题根源通常是本地仓库里残留了旧版本的_remote.repositories文件。3.2 用 dependency:tree 定位冲突命令与输出解读光看 pom.xml 很难判断最终生效的是哪个版本必须用dependency:tree把完整的依赖树打出来。# 输出完整依赖树 mvn dependency:tree # 只输出指定 groupId 的依赖过滤噪音 mvn dependency:tree -Dincludescom.example:common-lib # 输出到文件方便搜索 mvn dependency:tree -DoutputFiledeps.txt -DoutputTypetext输出里会出现omitted for duplicate和omitted for conflict两种标记。前者表示同一个 artifact 在同一个 pom 里被声明了多次后者表示版本冲突被调解掉了。看到omitted for conflict with 1.0.0就说明当前这个 2.0.0 被 1.0.0 替换了。# 典型输出片段 [INFO] - com.example:module-a:jar:1.0.0:compile [INFO] | \- com.example:common-lib:jar:1.0.0:compile [INFO] \- com.example:module-c:jar:1.0.0:compile [INFO] \- (com.example:common-lib:jar:2.0.0:compile - omitted for conflict with 1.0.0)要强制使用某个版本可以用dependencyManagement锁定或者用exclusions排除传递依赖。dependencyManagement dependencies dependency groupIdcom.example/groupId artifactIdcommon-lib/artifactId version2.0.0/version /dependency /dependencies /dependencyManagementdependencyManagement的优先级高于传递依赖的调解结果它相当于在依赖树的根部直接指定版本所有传递进来的同 artifact 都会被替换成这个版本。3.3 本地仓库与远程仓库的协作_remote.repositories 的作用Maven 在本地仓库的每个 artifact 目录下都会生成一个_remote.repositories文件记录这个包是从哪个远程仓库下载的。3.6.3 在解析依赖时会检查这个文件如果发现当前配置的仓库列表里没有记录中的仓库 id就会重新去远程拉取。# 查看某个 artifact 的 _remote.repositories 内容 cat ~/.m2/repository/com/example/common-lib/1.0.0/_remote.repositories # common-lib-1.0.0.jarcentral # common-lib-1.0.0.pomcentral如果换了私服地址但本地仓库里还留着旧仓库 id 的记录Maven 会认为本地包“来源不可信”触发重新下载。解决办法是删掉对应的_remote.repositories文件或者直接用-U参数强制更新。# 强制更新所有 SNAPSHOT 依赖 mvn clean install -U # 只清理某个 artifact 的本地缓存 rm -rf ~/.m2/repository/com/example/common-lib注意-U只对 SNAPSHOT 和未下载完整的包有效release 版本的包一旦下载成功就不会重复拉取除非手动删除。4. 生命周期与插件绑定把构建流程拆开看4.1 三套生命周期clean、default、site 的职责边界Maven 有三套相互独立的生命周期clean 负责清理default 负责构建site 负责生成文档。每套生命周期由多个 phase 组成phase 按顺序执行但不同生命周期之间互不干扰。clean 生命周期的 phase 只有三个pre-clean、clean、post-clean。default 生命周期最长从 validate 一直到 deploy核心 phase 包括 compile、test、package、verify、install、deploy。site 生命周期用得少主要是 site 和 site-deploy。# 执行 default 生命周期的 package 阶段 mvn package # 同时执行 clean 和 default 的 install 阶段 mvn clean install # 跳过测试 mvn clean package -DskipTests # 跳过测试编译和运行 mvn clean package -Dmaven.test.skiptrue-DskipTests只跳过测试运行测试代码仍然会编译-Dmaven.test.skiptrue连测试代码编译都跳过。两者在 CI 环境里的选择取决于你是否需要确保测试代码本身能编译通过。4.2 插件绑定与 goal 执行从 pom 到实际命令每个 phase 本身不做任何事真正干活的是绑定在 phase 上的插件的 goal。比如maven-compiler-plugin的compilegoal 绑定在 compile phase 上maven-surefire-plugin的testgoal 绑定在 test phase 上。build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.8.1/version configuration source1.8/source target1.8/target encodingUTF-8/encoding /configuration /plugin /plugins /buildsource和target指定编译的 JDK 版本encoding必须显式设为 UTF-8否则在中文环境下会出现乱码。3.6.3 默认绑定的 compiler 插件版本是 3.8.0如果项目里没有显式声明版本Maven 会从超级 pom 里继承默认版本。# 查看某个 phase 上绑定了哪些插件的 goal mvn help:describe -Dcmdcompile # 查看插件的详细配置参数 mvn help:describe -Dpluginorg.apache.maven.plugins:maven-compiler-plugin:3.8.1 -Ddetailhelp:describe是排查插件配置问题的利器它会列出插件所有可配置参数和默认值。当你发现某个配置项写了但没生效时先用这个命令确认参数名是否拼错。4.3 多模块项目的 reactor 顺序依赖决定构建次序多模块项目里Maven 会根据模块间的依赖关系自动计算构建顺序这个机制叫 reactor。如果模块 A 依赖模块 B那么 B 会先构建。如果存在循环依赖Maven 会直接报错。# 只构建指定模块及其依赖的模块 mvn -pl module-a -am clean install # 构建指定模块同时构建依赖它的模块 mvn -pl module-a -amd clean install # 跳过指定模块 mvn clean install -pl !module-b-pl指定模块列表-am表示同时构建依赖项-amd表示同时构建依赖此模块的模块。在大型项目里只构建改动模块能显著缩短 CI 时间。3.6.3 的 reactor 排序算法在并发构建时偶尔会出现顺序抖动如果发现构建顺序不符合预期可以用-T 1强制单线程。5. 避坑与排查3.6.3 最常见的五类翻车现场5.1 依赖下载卡住或超时镜像配置与网络排查现象mvn clean install卡在 Downloading 某一行不动几分钟后报Could not transfer artifact。原因镜像地址不可达或者私服返回了 302 重定向但 Maven 没有跟随。3.6.3 对重定向的处理比较保守某些反向代理配置下会直接失败。解决先用 curl 确认镜像地址是否可达再检查 settings.xml 里的 mirror 配置。如果私服需要认证确认server段里的用户名密码正确。# 测试镜像地址连通性 curl -I http://nexus.internal.com/repository/maven-public/ # 查看 Maven 实际使用的 settings mvn help:effective-settingshelp:effective-settings会输出合并后的最终配置能直接看到哪个 mirror 生效了。5.2 编译报错“无效的目标发行版”JDK 版本不匹配现象mvn compile报invalid target release: 1.8或无效的目标发行版: 11。原因Maven 使用的 JDK 版本低于 pom.xml 里配置的 source/target 版本。比如用 JDK 8 去编译 target 为 11 的项目。解决确认mvn -v输出的 Java version然后调整 JAVA_HOME 或修改 pom 里的编译版本。# 临时切换 JDK export JAVA_HOME/usr/lib/jvm/java-11-openjdk mvn -v如果系统里有多个 JDK建议用update-alternatives管理避免每次手动 export。5.3 测试用例在 3.6.3 下随机失败surefire 并发问题现象同一份代码本地跑测试通过CI 上偶发失败报错位置每次不同。原因surefire 插件默认的 forkCount 和 reuseForks 配置在 3.6.3 下对某些测试框架支持不完善导致测试类之间共享了静态状态。解决显式配置 surefire 的 fork 策略。plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-surefire-plugin/artifactId version2.22.2/version configuration forkCount1/forkCount reuseForksfalse/reuseForks /configuration /pluginreuseForksfalse表示每个测试类都新起一个 JVM能彻底隔离静态状态代价是测试时间变长。如果测试量不大这个配置能省掉很多排查玄学问题的时间。5.4 打包后资源文件丢失resources 插件配置遗漏现象mvn package生成的 jar 包里缺少application.yml或mapper.xml等资源文件。原因默认的 resources 目录只包含src/main/resources如果资源文件放在src/main/java下需要在 pom 里显式配置。build resources resource directorysrc/main/java/directory includes include**/*.xml/include include**/*.properties/include /includes /resource resource directorysrc/main/resources/directory /resource /resources /build这个配置在 MyBatis 项目里尤其常见mapper.xml 习惯和接口放在一起不配 resources 就会丢。5.5 私服认证失败settings.xml 加密与权限现象mvn deploy报 401 Unauthorized但用户名密码确认没写错。原因settings.xml 里的密码用了{加密串}格式但当前机器没有对应的 master 密码或者 server id 和 pom 里 distributionManagement 的 id 不一致。解决确认 server id 匹配必要时用mvn --encrypt-password重新生成加密密码。# 生成加密密码 mvn --encrypt-password # 输入明文密码输出加密串生成的加密串和settings-security.xml里的 master 密码配合使用缺一不可。6. 进阶技巧让 3.6.3 在 CI 里跑得更稳6.1 用 Maven Wrapper 锁定版本避免环境差异Maven Wrapper 能在项目里锁定 Maven 版本开发者克隆代码后直接./mvnw就能用指定版本构建不依赖本机安装的 Maven。# 在项目根目录生成 wrapper mvn -N wrapper:wrapper -Dmaven3.6.3 # 生成的文件 # mvnw mvnw.cmd .mvn/wrapper/maven-wrapper.propertiesmaven-wrapper.properties里会记录 distributionUrl指向 3.6.3 的下载地址。CI 环境里直接用./mvnw clean install能保证所有机器用的都是同一个 Maven 版本。6.2 构建缓存与离线模式加速重复构建CI 里最耗时的往往是依赖下载。把本地仓库目录缓存起来配合离线模式能大幅缩短构建时间。# 离线模式构建不检查远程更新 mvn clean install -o # 只对 SNAPSHOT 强制更新release 用缓存 mvn clean install -U离线模式要求本地仓库里所有依赖都已下载完整否则会直接失败。常见做法是CI 第一次构建用在线模式之后把~/.m2/repository缓存到对象存储后续构建先恢复缓存再用-o。6.3 构建扫描与依赖分析提前发现隐患3.6.3 本身不带依赖漏洞扫描但可以集成 OWASP dependency-check 插件。plugin groupIdorg.owasp/groupId artifactIddependency-check-maven/artifactId version8.4.0/version executions execution goals goalcheck/goal /goals /execution /executions /plugin执行mvn dependency-check:check会生成 HTML 报告列出所有已知漏洞的依赖。这个插件在 3.6.3 下运行正常但首次运行需要下载漏洞库耗时较长建议放在独立的 CI 阶段。6.4 参数调优MAVEN_OPTS 与并发构建大型项目构建时JVM 内存和并发线程数直接影响速度。# 设置 Maven 运行时的 JVM 参数 export MAVEN_OPTS-Xmx2g -Xms512m -XX:MaxMetaspaceSize512m # 并发构建4 个线程 mvn clean install -T 4 # 每个 CPU 核心一个线程 mvn clean install -T 1C-T 4表示 4 个线程并行构建-T 1C表示每个 CPU 核心一个线程。并发构建对多模块项目效果明显但如果模块间依赖复杂可能出现 reactor 排序问题建议先用-T 1确认构建顺序正确后再开并发。我自己的习惯是本地开发用-T 1C加速CI 上先用-T 1保证稳定性等构建时间成为瓶颈再逐步调大并发数。这套组合在 3.6.3 上跑了几年除了偶尔的本地仓库缓存问题基本没出过大的幺蛾子。希望帮到你。本文还有配套的精品资源点击获取