
1. 为什么说 Windows 部署 Maven 和 Gradle 不是“解压加 PATH”这么简单我见过太多刚转 Java 或刚接触 Android 的同事第一次在 Windows 上配构建工具时都以为这就是个“下载、解压、配环境变量、跑mvn -v”的体力活。运气好的人十分钟搞定运气不好的人能从下午折腾到深夜最后发现卡在的往往不是 Maven 或 Gradle 本身而是 JDK 版本对不上、环境变量有多个残留、仓库下载超时、控制台中文乱码这些看着不起眼的问题。Maven 和 Gradle 是整个 Java/Android 生态的骨架。Maven 管依赖、管构建生命周期Gradle 在 Maven 的基础上把构建脚本变成了一种编程语言尤其在 Android 和 Spring 项目里几乎成了默认选择。而 Windows 又和 macOS、Linux 不一样它没有包管理器去统一处理软件安装环境变量靠图形界面手工配控制台默认编码是 GBK路径还动不动带空格带中文。这些特性叠加在一起就会让一个看似简单的部署过程变成一场小型的“环境考古”。这篇内容我会按自己实际部署的顺序来写先处理 JDK 这个底层依赖再分别讲 Maven 和 Gradle 的完整安装与配置然后补上国内镜像、乱码、JVM 版本冲突这几个 Windows 用户最容易踩的坑。适合刚接触 Java 构建工具的新人也适合需要在多台 Windows 机器上快速部署环境的同学参考。文中的路径、版本号都来自我个人常用的组合你完全可以根据自己的机器情况替换关键是理解每一步背后的逻辑。2. 先解决 JDK 这个底层依赖JAVA_HOME 与版本对应关系2.1 排查系统里的 JDK 到底有几个很多人在部署 Maven 和 Gradle 之前根本不确认自己机器上的 Java 环境是否干净。最常见的现象是在命令行里敲java -version有输出就以为 JDK 没问题了。但注意你敲出来的java可能来自 JRE也可能来自某个被 PATH 环境变量“抢注”的旧版本 JDK而不是你真正想用的那一个。我的建议是打开 CMD 或 PowerShell依次执行这三条命令java -version javac -version echo %JAVA_HOME%如果java -version有输出但javac提示找不到说明你的 PATH 里指向的很可能只是 JRE或者 JDK 的bin目录没有被完整加入 PATH。如果javac能正常输出但echo %JAVA_HOME%为空说明你虽然能编译但 Maven 和 Gradle 这类工具在启动时依赖JAVA_HOME来定位 JDK这个变量为空必然会导致后续报错。还有一种隐蔽情况是机器上装了多个 JDK命令行里敲java走的是一套IDEA 里配的又是另一套。可以用where java查看命令实际解析路径如果出现多个结果就要注意 PATH 中的顺序——排在前面的会先被命中。2.2 JAVA_HOME 的正确配置方式JAVA_HOME 这个环境变量规则很简单指向 JDK 的安装根目录不能带着bin也不能带分号。比如我习惯把 JDK 放在D:\dev\jdk-17那 JAVA_HOME 就填D:\dev\jdk-17然后在 PATH 里追加%JAVA_HOME%\bin。在 Windows 上有两个地方可以配环境变量用户变量和系统变量。建议配在用户变量里因为用户变量不需要管理员权限而且不会影响同一台机器上的其他账户。但是有个细节如果系统变量里已经存在一个旧 JAVA_HOME用户变量不会覆盖它系统变量优先级更高。所以配完之后一定要重新echo %JAVA_HOME%确认。配置完环境变量后当前已经打开的 CMD 窗口不会自动刷新必须重新打开一个新窗口或者手动执行refreshenv如果装了 Chocolatey。这一步是无数人反复测试却看不到效果的头号原因。2.3 Maven、Gradle 与 JDK 的版本匹配表部署构建工具前先确认你自己的项目需要哪个版本的 JDK再倒推 Maven 和 Gradle 的版本。网上经常有人问“为什么 Gradle 突然报错说版本不兼容”十有八九是 JDK 版本和 Gradle 版本不匹配。我整理了一个常用对照关系构建工具最低 JDK说明Maven 3.6.xJDK 8老项目比较常见JDK 17 上也能跑Maven 3.9.xJDK 8当前推荐版本对新项目支持更好Gradle 7.xJDK 8很多 Android 老项目的标配Gradle 8.xJDK 8运行/ 17当前主流兼容性较均衡Gradle 9.xJDK 17较新的项目或新版本 AGP 会用到如果你装的 Gradle 是 9.x但环境变量指向的是 JDK 8启动时基本会直接报错。报错信息里会明确告诉你“Unsupported class file major version”或者更直接地指出 Gradle 版本需要什么 JDK。反过来如果你用 Gradle 6.7.1 这种老版本JAVA_HOME 指向 JDK 17也会出现不兼容的提示。这种时候最正确的思路不是硬换 JDK而是优先查看项目的gradle/wrapper/gradle-wrapper.properties看看项目期望的 Gradle 版本缺什么补什么。3. Maven 部署全程下载、环境变量、settings.xml 一个都不能少3.1 下载二进制包并解压到无中文路径Maven 的官方下载入口在 Apache 官网进入项目页后选择 bin.zip 后缀的压缩包即可不需要源码包。下载解压这个环节我强烈建议把解压目标放到一个没有中文、没有空格的路径下比如D:\dev\apache-maven-3.9.9。Windows 对中文路径的兼容性这些年虽然好了很多但 Maven 构建时如果项目依赖的某个插件对路径处理不严谨中文路径会引发各种莫名其妙的“找不到文件”或编码报错。为了省事从一开始就避开它。另外Windows 资源管理器自带的 zip 解压能力足够用不需要额外装解压软件但解压后注意检查目录结构确认bin、conf、lib目录都在且没有多套一层同名的嵌套目录。3.2 配置 MAVEN_HOME 与 PATHMaven 需要两个环境变量一个是MAVEN_HOME有些老教程也写M2_HOME新版本两者均可但推荐统一用MAVEN_HOME值为 Maven 的解压根目录另一个是在 PATH 中追加%MAVEN_HOME%\bin。具体操作路径是右键“此电脑” - 属性 - 高级系统设置 - 环境变量。在用户变量区域点击“新建”变量名填MAVEN_HOME变量值填D:\dev\apache-maven-3.9.9。然后在用户变量的 PATH 中点击“编辑”新建一行填%MAVEN_HOME%\bin。之后重新打开 CMD输入mvn -v如果能看到 Maven 版本、Java 版本和系统路径信息说明这一步已经通过。3.3 settings.xml 里的本地仓库设置Maven 部署中真正拉开新手和老手差距的是conf/settings.xml这个文件。它放在 Maven 解压目录的conf下控制着本地仓库位置、镜像地址、代理等全局行为。第一次用 Maven 时默认会在C:\Users\你的用户名\.m2\repository下建本地仓库。如果 C 盘空间紧张或者你希望重装系统后还能保留之前下载的依赖最好把它改到其他盘。打开settings.xml找到localRepository这个配置默认是被注释掉的长这样localRepository/path/to/local/repo/localRepository把它替换成你想要的路径例如localRepositoryD:\dev\maven-repo/localRepository改完后没有任何输出提示但你可以通过执行mvn help:evaluate -Dexpressionsettings.localRepository来验证输出结果如果是你设置的路径就说明生效了。这一步很多人会跳过结果就是依赖下载几 GB 全部塞进 C 盘等发现时系统盘已经飘红。3.4 用 mvn clean install 验证全链路配置完成后不要只满足于mvn -v建议立刻拿一个真实项目跑一次完整构建。常用的命令组合是mvn clean install -DskipTests这条命令会清理 target、编译源码、执行单元测试跳过、打包并安装到本地仓库。第一次执行时 Maven 会从中央仓库拉取大量插件依赖耗时较长如果卡在进度条上不动先检查网络然后再确认是否配置了镜像。关于镜像配置我会在后面的章节单独展开。有一个细节值得注意mvn clean install和mvn clean install -DskipTests的区别不只在于跳不跳测试。-DskipTests是编译测试代码但不执行-Dmaven.test.skiptrue是连测试代码都不编译。如果你遇到“测试代码编译不过导致打包失败”前者无法解决需要后者。mvn clean install -DskipTests -Dmaven.test.skiptrue如果你的项目是多模块结构可以加-pl指定模块-am表示同时构建依赖模块mvn clean install -pl module-a -am -DskipTests4. Gradle 部署全程命令行工具、用户目录与 Wrapper 机制4.1 下载 Gradle 与配置 GRADLE_HOMEGradle 的官方下载入口在 Gradle 官网的 releases 页面选择当前需要的版本下载 binary-only 或 complete 压缩包。Binary-only 是纯可执行文件complete 会附带源码和文档。作为日常开发binary-only 就够用体积也小不少。解压后同样放到无中文路径例如D:\dev\gradle-8.10。配置两个环境变量GRADLE_HOME指向该目录PATH追加%GRADLE_HOME%\bin。操作方式与 Maven 完全一致这里不再重复。验证方式是打开新 CMD执行gradle -v会输出 Gradle 版本、JVM 版本和操作系统信息。如果能看到这些内容命令行工具就装好了。4.2 理解 .gradle 用户目录和 Maven 的.m2类似Gradle 也有自己的用户级目录默认在C:\Users\你的用户名\.gradle。这个目录里主要缓存三样东西依赖缓存caches/modules-2、项目 wrapper 下载的 Gradle 发行版、以及全局初始化脚本init.gradle。注意Gradle 的缓存结构和 Maven 完全不一样。Maven 本地仓库是一堆groupId/artifactId/version目录jar 包直接放在里面Gradle 的caches则有一套自己的索引机制你很难直接通过文件管理器找到某个 jar 包。所以直接把 Maven 仓库路径塞给 Gradle 是不成立的。如果你希望把 Gradle 的缓存目录也挪到非 C 盘可以通过环境变量GRADLE_USER_HOME指定。例如设置GRADLE_USER_HOMED:\dev\gradle-cache之后所有缓存的依赖都归到该目录下。这个变量建议配在用户变量中避免影响系统全局。4.3 Wrapper 机制为什么配好了 Gradle 还要下载一次很多人配置完 Gradle 后进入某个项目执行gradle build却发现它又自动下载了一个 Gradle 发行版于是以为安装失败。其实这是 Wrapper包装器机制在起作用。现代项目通常会在代码仓库里带上gradlew和gradlew.batWindows 执行脚本以及gradle/wrapper/gradle-wrapper.properties。当你执行gradlew.bat时脚本会读取gradle-wrapper.properties里的distributionUrl然后去指定位置下载对应版本的 Gradle。也就是说项目里的构建过程更依赖 Wrapper 指定的版本而不是你机器上全局安装的那个版本。这是为了保证团队所有成员使用相同版本的 Gradle 构建避免“在我机器上能跑”的尴尬。如果你的网络条件不好Wrapper 下载 Gradle 发行版会非常慢此时可以手动修改gradle-wrapper.properties文件把distributionUrl替换成国内镜像地址或者提前把对应版本的 Gradle 压缩包下载好放入~/.gradle/wrapper/dists对应的目录让 Wrapper 跳过下载步骤。4.4 用 gradle -v 和 gradle init 验证验证命令行工具除了gradle -v我更推荐跑一次gradle init。它会交互式地引导你生成一个基础项目骨架整个过程能实际验证 Gradle 的脚本执行、依赖解析和目录生成是否正常。生成完可以执行gradle build看仓库镜像是否配置正确。如果你直接进入一个已有项目执行gradle build遇到下载超时也不容易分清是 Wrapper 问题还是仓库问题先用gradle init排除一大部分干扰项。5. 国内镜像配置阿里云仓库在 Maven 与 Gradle 中的落地姿势5.1 Maven 的阿里云镜像配置在国内直接连 Maven 中央仓库下载速度相当不稳定尤其是遇上插件和依赖多一点的项目经常卡在某个 jar 包上转圈。解决方案就是配置镜像。现在国内比较常用的是阿里云 Maven 镜像它的公共地址是https://maven.aliyun.com/repository/public这个地址聚合了中央仓库和常用的第三方仓库。在settings.xml的mirrors节点中加入如下内容mirrors mirror idaliyun/id namealiyun public/name urlhttps://maven.aliyun.com/repository/public/url mirrorOf*/mirrorOf /mirror /mirrorsmirrorOf的值*表示所有仓库请求都走这个镜像。如果你只在中央仓库这一个仓库上拉取依赖这是最简单粗暴的方案。但如果你公司里有私有 Nexus 或 Artifactory*会把私有仓库的请求也拦截掉反而出问题。更稳妥的做法是只镜像centralmirrorOfcentral/mirrorOf这样其他仓库定义不会被影响私有仓库依然走原地址。除了public阿里云还提供多个细分仓库常见的有镜像 ID对应的仓库aliyun-centralhttps://maven.aliyun.com/repository/centralaliyun-publichttps://maven.aliyun.com/repository/publicaliyun-googlehttps://maven.aliyun.com/repository/googlealiyun-gradle-pluginhttps://maven.aliyun.com/repository/gradle-pluginGoogle 仓库对 Android 项目非常重要因为 AndroidX、AGP 等依赖默认都发布在google()仓库如果 Maven 配置里没有覆盖到它Android 项目依然会卡住。5.2 Gradle 的仓库与 init.gradle 配置Gradle 项目的仓库配置写在build.gradle里。默认写法通常是repositories { mavenCentral() google() }这里mavenCentral()会走 Maven 中央仓库google()走 Google 的 Maven 仓库。在国内环境直接跑下载速度依然是个问题。阿里云同样提供了对应的镜像地址可直接替换repositories { maven { url https://maven.aliyun.com/repository/public } maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/gradle-plugin } }但如果你不想在每个项目里都手动改一遍可以在~/.gradle/init.gradle或init.d目录下的.gradle文件里写全局配置。init.gradle会被 Gradle 在启动时自动读取作用于所有项目。一个常用的全局初始化脚本如下allprojects { repositories { maven { name aliyun-public; url https://maven.aliyun.com/repository/public } maven { name aliyun-google; url https://maven.aliyun.com/repository/google } maven { name aliyun-gradle-plugin; url https://maven.aliyun.com/repository/gradle-plugin } mavenCentral() } }这样写有一个注意点allprojects里的配置会对每个项目生效但项目自身build.gradle中的repositories如果定义了其他仓库两者会叠加而不是覆盖。如果项目中使用了mavenLocal()指向本地 Maven 仓库它依然会生效。用这种全局脚本的好处是以后不管开多少个新项目仓库加速都是默认配置。5.3 镜像配置中的常见错误我见过不少新手在配置镜像后反而拉不到依赖排查后发现是以下原因第一settings.xml中的mirrorOf写成了mirrorOf*/mirrorOf导致公司私有仓库的包也全部去阿里云找。如果你公司内部通过私有仓库发布了一些公共组件这种全局镜像会让它们全部拉取失败。解决方法是把私有仓库单独配置或用external:*表示只镜像外部仓库内部地址不走镜像。第二切了镜像后旧依赖的失败缓存还在本地。Maven 下载失败时会在本地仓库生成.lastUpdated结尾的临时文件这个文件会让 Maven 在一段时间内“记住”该依赖下载失败即使你切了镜像也不会重新尝试。遇到这种情况要么删掉本地仓库中对应的.lastUpdated文件要么执行强制更新mvn clean install -U第三Gradle 的依赖缓存也会缓存失败的解析结果。如果你在 Gradle 中改了仓库地址建议执行gradle build --refresh-dependencies或者直接清理~/.gradle/caches中对应的缓冲目录。6. 中文乱码与 Gradle JVM 版本冲突Windows 高频故障排查6.1 中文乱码的根因Windows 上的中文乱码问题非常典型几乎每个用 Maven 或 Gradle 构建包含中文内容项目的开发都会遇到。根本原因是编码不一致Windows 简体中文版默认使用 GBKCP936作为控制台编码而 JDK 17 及以后版本默认使用 UTF-8 作为文件编码。于是你在pom.xml里写的注释、在构建脚本里输出的中文日志、在测试报告里生成的中文文件名都可能在控制台或日志文件里变成乱码。举个例子Gradle 脚本里写println 构建成功控制台显示出来的可能是“鏋勫缓鎴愬姛”这类完全看不懂的字。这个不是程序逻辑的问题而是输出方JVM 编码 UTF-8和控制台解码方GBK不匹配导致的。6.2 解决乱码的几个关键设置优先修改gradle.properties给 Gradle JVM 指定统一的文件编码。在项目根目录的gradle.properties中加入org.gradle.jvmargs-Dfile.encodingUTF-8但注意这一项只影响 Gradle 自身的 JVM 和它启动的 daemon 进程如果项目里还有独立的 JavaExec 任务或 Test 任务它们各自启动的 JVM 可能仍使用默认编码。更彻底的方案是在build.gradle里显式声明测试和 Java 编译任务的编码tasks.withType(JavaCompile) { options.encoding UTF-8 } tasks.withType(Test) { systemProperty file.encoding, UTF-8 }Maven 项目则在pom.xml中通过project.build.sourceEncoding属性指定properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties控制台本身的代码页也可以改。在 CMD 里执行chcp 65001可以切换当前窗口编码到 UTF-8但这是临时的重启窗口就失效。PowerShell 里则可以用[Console]::OutputEncoding [System.Text.Encoding]::UTF8如果你希望整个系统层面统一可以在 Windows 的“区域设置”中勾选“Beta使用 Unicode UTF-8 提供全球语言支持”重启后系统的非 Unicode 程序编码会切换到 UTF-8。但这个改动会影响其他老软件建议在开发机上谨慎使用。6.3 Gradle 版本与 JVM 版本不兼容的排查网上经常能刷到这类报错The projects Gradle version 6.7.1 is incompatible with the Gradle JVM version这句话的含义是项目通过 Wrapper 锁定了 Gradle 6.7.1但当前 Gradle 运行在 JDK 17 上而 Gradle 6.7.1 不支持 JDK 17。遇到这种问题先看项目的gradle/wrapper/gradle-wrapper.properties确认锁定的 Gradle 版本再看当前 JAVA_HOME 指向的 JDK 版本。两者匹配就没事不匹配时选择改 Wrapper 的 Gradle 版本或者临时切换 JDK。如果你不方便改 JDK可以在项目的gradle.properties里显式指定 Gradle 运行所需的 JDK 路径org.gradle.java.homeD:\\dev\\jdk-11这个配置的优先级高于 JAVA_HOME适合那种“系统默认 JDK 17但某个老项目必须用 JDK 11”的场景。注意路径中的反斜杠在 properties 文件里需要转义写成双反斜杠。6.4 IDEA 里配置 Maven 和 Gradle 的落地步骤IDEA 是绝大多数 Java 开发者的主战场环境变量配好之后还要让 IDEA 正确识别 Maven 和 Gradle。打开 IDEA 的设置在“Build, Execution, Deployment”下能找到 Maven 和 Gradle 两块配置。Maven 设置关键有三项Maven home path、User settings file、Local repository。前两项通常会自动检测但如果检测到的是 IDEA 自带的 Maven或者 user settings 文件指向了默认的~/.m2/settings.xml而你实际上把 settings.xml 放在了别的目录就需要手动改成你真正使用的路径。Local repository 会从 settings.xml 里自动读取如果显示不对检查一下 settings.xml 的语法。Gradle 设置中最常用的是“Gradle user home”和“Gradle JVM”。Gradle user home 默认指向~/.gradle如果你配了GRADLE_USER_HOME环境变量IDEA 会自动读到。关键在“Gradle JVM”下拉框它决定导入项目时 Gradle 运行在哪个 JDK 上。很多老项目导入时报错就是因为这里选成了 JDK 17但项目 Wrapper 使用的 Gradle 6.7.1 不支持。IDEA 在导入 Gradle 项目时还有一个选项“Use Gradle from: ‘gradle-wrapper.properties file’”强烈建议选择这一项。它会让 IDEA 完全尊重项目里锁定的 Gradle 版本而不是用你全局装的 Gradle 版本从而减少一大部分“IDEA 里能跑命令行跑不了”的问题。7. 两个工具共存的本地仓库策略与我的实战建议7.1 Maven 本地仓库与 Gradle 缓存的区别装了 Maven 又装 Gradle 之后很多人最大的困惑是这两个工具的本地仓库能不能共用答案是不建议直接共用。Maven 的本地仓库默认在~/.m2/repository目录结构是groupId/artifactId/version这种可读性很强的层级。Gradle 的默认缓存则在~/.gradle/caches它有自己的索引、锁文件和缓存元数据目录结构不是给人类直接浏览设计的。如果把 Gradle 的mavenLocal()仓库指向 Maven 的~/.m2/repositoryGradle 确实能读取 Maven 下载过的 jar但反过来Maven 无法直接使用 Gradle 的缓存。而且如果两个工具的版本策略不同同一个依赖在 Maven 里和 Gradle 里解析出的可选版本集合可能不一致这会引发“本地有构建却拉不到”的错觉。所以在日常开发中我给的建议是Maven 和 Gradle 的本地缓存目录保持独立不做硬链接或符号链接。7.2 让 Gradle 通过 mavenLocal() 读取 Maven 仓库如果项目比较特殊比如你的团队统一用 Maven 发布内部组件而另一个项目用 Gradle 构建想让 Gradle 识别 Maven 本地仓库里已安装的组件可以在 Gradle 的repositories中加入repositories { mavenLocal() mavenCentral() }mavenLocal()会让 Gradle 去读取~/.m2/repository。注意顺序很重要mavenLocal()写在前面说明本地仓库优先级更高Gradle 会优先使用本地已经存在的依赖避免重新下载如果写在后面本地仓库的优先级反而会被降低。不过这个配置有一个前提就是本地 Maven 仓库中确实已经通过mvn install把对应依赖安装进去了否则 Gradle 照样会去远端拉取。7.3 最后再分享一个安装后的验证习惯前面讲了很多步骤最后说一个我自己的习惯装完 Maven 和 Gradle我不会只跑mvn -v和gradle -v就收工而是会用一个包含较多依赖的真实项目完整跑一次构建然后立刻把本地仓库目录截图或者记下大小。目的有两个一是确认镜像配置真的生效二是给后续排查留下基准数据。如果哪一天构建突然变慢或者依赖解析失败对比一下仓库目录的增量往往能快速定位问题。另外Windows 上配置环境变量后记得养成“新开一个窗口再验证”的习惯。我曾经因为几个旧窗口里缓存了错误的环境变量反复确认自己的配置没写对最后发现变量配置完全没问题纯粹是窗口没刷新。这种小问题看起来不起眼但在 Windows 上出现频率极高。部署这件事本身不难但要做到一次配置、长期稳定关键还是理解 JDK 版本、构建工具版本、仓库镜像这几个环节之间的依赖关系。按照这个顺序捋一遍Windows 环境下的 Maven 和 Gradle 基本就不会再给你添乱了。