概述
Maven Wrapper,缩写为mvnw
,是一个受Gradle Wrapper和Takari Wrapper启发而产生的Maven子项目,主要有以下三个用途:
- 让开发者电脑上无需安装Maven,也不用配置环境变量,即可使用Maven构建项目;
- 团队开发时,可让每个开发人员都保持一致的Maven版本;
- 极个别情况下,某些特殊项目,必须使用某个指定的Maven版本;其他项目使用安装配置指定的Maven版本,不互相干扰。
另外还有一款mvnd
工具和命令行,参考比Maven快2~10倍的编译工具mvnd简介与实战。
安装
安装Maven Wrapper最简单的方式是在项目(注意,可以不是Maven项目,跟Maven Wrapper版本号有关,至少需要是3.3.0;下面截图可看到并没有pom.xml
文件)的根目录下运行安装命令mvn wrapper:wrapper
:
两个mvnw
脚本文件的日期都是2024年5月21日,是Maven Wrapper 3.3.2版本的发布日期。
默认使用最新版本的Maven Wrapper,当前最新版是3.3.2
。
另外Maven Wrapper默认下载配置好的Maven,也就是mvn -v
命令依赖的版本:
如果想指定Maven版本,则追加版本号,即使用命令mvn wrapper:wrapper -Dmaven=3.9.9
指定下载3.9.9
版本。
另,Maven马上迎来4这个大版本,拭目以待吧。
根据Maven Wrapper版本的不同,会在当前目录下生成3或4个文件:
-
.mvn\wrapper\maven-wrapper.jar
:.mvn\wrapper
隐藏目录下的JAR包,没有显式告知版本号。通过JD-GUI等反编译工具当然可以查找到版本号。3.3.2版本(自什么版本开始,暂时未知)的Maven Wrapper不再生成此文件(下文有原因分析)。 -
.mvn\wrapper\maven-wrapper.properties
:配置文件,内容如下,据此可知使用的Maven和Maven Wrapper版本,可解答上面的问题。注:Maven Wrapper有些版本此文件只有一个distributionUrl,无法一眼看出Wrapper版本,这是Takari Maven Wrapper。
另外,如果不再生成JAR包,则maven-wrapper.properties
配置文件也会不一样:
-
mvnw
:用于Linux、Mac等系统的脚本; -
mvnw.cmd
:用于Windows系统的脚本;
下载安装
因为执行mvn wrapper:wrapper
命令安装Maven Wrapper需提前下载并配置好Maven。如果开发者电脑里没有Maven,咋办呢。
参考 stackoverflow,从 这里选择一个版本,推荐最新版。一般直接下载第一个文件即可,形如maven-wrapper-distribution-x.x.x-bin.zip
的压缩文件。解压缩,然后手动添加.mvn/wrapper/maven-wrapper.properties
配置文件并指定想要使用的Maven版本(参考上文);提交到Git Server前,最好删除压缩包里的JAR文件。
确实有点麻烦。
Takari
上面提到JD-GUI可反编译maven-wrapper.jar
文件。值得注意的是,除了Apache Maven Wrapper外,还有个Takari Maven Wrapper:
上面截图,Takari Maven Wrapper使用的包路径是apache。
在Maven仓库里搜索,最后一个GAV如下:
时间停留在2019年12月4日。
这两者的关系是:Maven Wrapper,最初由Takari团队开发。后来这个工具被广泛采纳和使用,成为Maven官方推荐的工具。因此之前的GAV不再发布新版本,最新的GAV如下:
版本号和上面提到的3.3.2对应得上。
版本控制系统
可能有些朋友会有疑问,使用Maven Wrapper无需下载安装Maven,更无需配置环境变量,但上面却使用mvn wrapper:wrapper
命令来安装Maven Wrapper,mvn
命令的使用不是得提前安装并配置Maven么。
是不是有种鸡生蛋,还是蛋生鸡的既视感?
实际上,执行mvn wrapper:wrapper
命令生成的几个文件,是需要一并提交到Git版本管理系统里。也就是说,从GitHub或GitLab上clone下来的某个项目。如果有这几个文件,则不管你是否安装并配置过Maven,都会使用此项目指定的Maven版本,也就是Maven Wrapper通过配置文件maven-wrapper.properties
指定的版本:
从截图可知,默认在C盘用户目录下安装zip文件并解压,然后设置MAVEN_HOME
指向解压缩后的目录。另外,最新版Maven Wrapper,即3.3.2版本会删除压缩包,毕竟留着也没用。反过来,正是因为这几个文件需要提交到Git Server,如Maven Wrapper早期版本会生成JAR包,此文件说大不大,说小也并不小,40~50k左右。
Git版本管理系统有一个最佳实践是:提交代码、配置等文本文件,尽可能不要提交图片、JAR包等二进制文件。
有些朋友可能会说,就那么50k,也没有什么大碍吧。编码洁癖强迫症,像我就是,真受不了。
或者,有其他朋友会说,这些二进制文件并不会更新,一次性提交,后期不会更新,不要紧的。
等等,真的不会更新吗?
指定Maven Wrapper版本
不管是使用Takari Maven Wrapper,还是使用Apache Maven Wrapper,就和使用其他三方依赖,如Spring Boot版本号,肯定是尽可能使用次新版,毕竟新版本修复若干问题和优化若干性能等。
然后上面提到的命令行追加的版本号针对的是Maven的版本。如果想要使用某个版本的Maven Wrapper,而不是最新版的Maven Wrapper,该怎么做呢。
还是用命令行,此处对一个空的文件夹执行命令mvn wrapper:wrapper:3.1.0
:
执行失败,报错如下:
No plugin descriptor found at META-INF/maven/plugin.xml
。什么鬼,我想下载的是3.1.0版本,怎么会是\wrapper\wrapper\3.0.3\wrapper-3.0.3.jar
这个JAR,去D盘一看,确实有这个JAR,Maven仓库也有这个JAR:
这里给出一个未加证实的猜测:Maven命令执行时从Maven仓库下载指定版本的JAR,如果没有指定版本的JAR,会做降级处理,下载低版本的JAR。比如我想要的是3.1.0版本,结果会下载3.0.3,毕竟Maven仓库里只有这个版本。至于那个报错,No plugin descriptor
是Maven Wrapper的插件扫描机制的报错。
稍加分析,不难得知,mvn wrapper:wrapper
是经过缩写的命令行,如果需要指定版本,必须写全GAV信息,即groupId、artifactId、version。但是很不幸,还是相同的报错:No plugin descriptor found at META-INF/maven/plugin.xml
。
经过各种反复试错,以及对报错的分析plugin.xml
,加上之前经常看到maven-xxx-plugin
:
于是想到执行命令:mvn org.apache.maven.plugins:maven-wrapper-plugin:wrapper
,终于执行成功:
可以看到命令执行成功,虽然还是使用最新版Maven Wrapper;但是,至少我们知道mvn wrapper:wrapper
是mvn org.apache.maven.plugins:maven-wrapper-plugin:wrapper
命令的缩写。
现在需要做的就是加上版本号。
姿势:mvn org.apache.maven.plugins:maven-wrapper-plugin:3.1.0:wrapper
。
经过试错,发现早期版本的Apache Maven Wrapper(区别于Takari Maven Wrapper)不支持在非Maven工程目录下执行mvn wrapper:wrapper
:
直到3.3.0版本,才开始支持对非Maven项目(没有pom.xml
文件)执行mvn wrapper:wrapper
:
经过验证,Apache Maven Wrapper第一个版本,即3.1.0,可从Maven仓库搜索得知,还是存在maven-wrapper.jar
这个JAR文件:
后面Apache Maven团队优化这个问题,即执行mvn wrapper:wrapper
命令时不生成JAR文件,当然就不用提交到Git Server。JAR文件也参与到版本控制系统里,确实很不合适。
org.apache.maven.plugins:maven-wrapper-plugin和org.apache.maven.wrapper:maven-wrapper
上面在诸般尝试切换Maven Wrapper版本时,发现两个很类似的GAV:
以及
两者都可在Maven参考搜索,但版本号有些许不同。那有什么区别呢?
org.apache.maven.plugins:maven-wrapper-plugin
:如命名所示,这是一个Maven插件,用于帮助项目创建Maven Wrapper;通过Maven插件的方式执行,可生成mvnw
脚本和.mvn
目录。
org.apache.maven.wrapper:maven-wrapper
:是Maven Wrapper的实际实现,包含Maven Wrapper的核心逻辑和脚本文件,用于包装和管理Maven的版本。maven-wrapper
是在mvnw
脚本中调用的库,负责下载和执行指定版本的Maven。它确保即使在没有预先安装Maven的情况下,项目也能使用指定的Maven版本来进行构建。
前者是插件,后者是具体的实现类库。
其他诸多Maven插件和实现库,原理类似。
升级Maven Wrapper
上面也提到更高版本更加合理,对一个传统的非Maven项目执行mvn wrapper:wrapper
变成Maven项目,是很合理的需求及业务场景。
所以,升级Maven Wrapper也是理所当然的。
对一个已经使用Maven Wrapper的Maven项目,执行命令mvn wrapper:wrapper
会升级Maven Wrapper版本到最新,升级还是涉及那几个文件:两个脚本文件、一个配置文件、一个可选的JAR文件。如果指定版本号,则升级到指定的版本号。
也就是说,使用早期版本的Maven Wrapper(所有Takari版本的Maven Wrapper,以及早期的Apache Maven Wrapper),执行命令会更新JAR,这个JAR是需要提交到Git Server的。Maven团队也意识到这个问题,在某个版本后,执行命令不再生成JAR文件,只剩下3个文件。
不同版本的潜在问题
上面提到Maven Wrapper可以使得同一个开发团队里的不同开发者使用相同版本的Maven。不过,鄙人用了8年多Maven,也没怎么去关注过CI/CD系统使用的Maven版本,倒一直没有遇到Maven版本不同带来的任何问题。
只能说,使用不同的大版本(如Maven 3.3和Maven 3.6)的Maven,可能存在如下潜在的问题:
- 构建结果不一致:不同的 Maven 版本可能对插件、依赖解析、配置处理等有不同的行为。如果开发者使用的 Maven 版本不一致,可能会导致在不同机器上构建相同项目时产生不同的结果。这种不一致会在 CI/CD 流水线中表现得更加明显,特别是在构建成功与失败的标准上;
- 插件兼容性问题:Maven 插件可能针对特定的 Maven 版本进行开发和优化。使用较新的 Maven 版本可能会默认启用或弃用一些功能,而较旧的 Maven 版本则可能不支持这些功能,导致插件无法正常运行或行为不一致;
- 性能差异:Maven 的不同版本在性能上可能有显著差异。新版本通常会修复一些性能问题或引入更高效的依赖管理机制,但旧版本可能仍然存在这些性能问题,这会导致构建时间上的差异,进而影响开发效率;
- Bug 和功能特性:新版本的 Maven 通常修复了一些已知的 bug,并引入了新的功能。如果团队成员使用的版本不一致,可能会导致部分成员无法使用新功能,或者某些问题在新版本中已被修复但在旧版本中仍然存在,这会导致团队协作中的额外沟通和问题排查成本;
- 配置文件解析:Maven 的某些配置文件解析逻辑可能在不同版本中有所不同,尤其是在处理复杂的继承结构、Profile、插件配置等方面。这会导致同一套配置文件在不同的 Maven 版本中产生不同的解析结果,从而影响构建过程;
- 依赖解析问题:Maven 版本之间的依赖解析算法可能会有所不同。新版本可能会更智能地处理冲突和版本解析,而旧版本可能存在解析错误或不完善的地方。这种差异会导致开发者在解析依赖时遇到不同的问题,甚至导致构建失败。
推荐和CI系统使用的Maven保持一致,或引入Maven Wrapper。
使用脚本
以Windows操作系统为例,双击mvnw.cmd
文件,会根据.mvn\wrapper\maven-wrapper.properties
文件里的distributionUrl,去指定的地方(https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/,这个地址基本上固定不变)下载指定版本的Maven压缩包(如上是3.8.6
版本),存储到C盘用户目录下,如C:\Users\johnny\.m2\wrapper\dists\apache-maven-3.8.6-bin\1ks0nkde5v1pk9vtc31i9d0lcd
。随机生成的文件夹。
国内网络因为一些原因,下载速度可能会非常慢(不使用VPN,浏览器打开上述下载地址,速度还行;命令行会慢很多)。下载成功后,解压缩zip包,解析项目pom.xml文件,开启新一轮的漫长等待:
默认情况下,三方依赖JAR会下载到C:\Users\johnny\.m2\repository
下。
切换源
上面提到Maven Wrapper速度慢(应该是仅大陆用户吧)的问题:
- 下载Maven压缩zip包
- 下载
pom.xml
文件引入的依赖
针对第一个问题,可考虑修改配置文件maven-wrapper.properties
里的distributionUrl指向阿里云或别的镜像仓库:https://mirrors.aliyun.com/apache/maven/maven-3/
阿里云只维护这么几个Maven版本,少得有点过分:
针对第二个问题,
一般本机安装Maven时,都会在conf/settings.xml
中配置maven的国内源,但使用Maven Wrapper时并没有看到类似setting.xml
的配置文件。此时可在项目pom.xml
中直接指定国内源。在公司开发多个项目时,则可考虑在parent父工程里的pom.xml文件里添加类似配置:
mvnw.cmd
还是以Windows系统为例,最新版(3.3.2版本)的mvnw.cmd
文件,去掉License相关注释(@REM
开头的行就是注释)后如下: