Maven
Maven
Maven 是什么
Maven 是一个项目管理工具,它包含了一个项目对象模型(Project Object Model),反映在配置中,就是一个 pom.xml 文件。它是一组标准集合,一个项目的生命周期、一个依赖管理系统,另外还包括定义在项目生命周期阶段的插件(plugin)以及目标(goal)。
当我们使用 Maven 的使用,通过一个自定义的项目对象模型pom.xml 来详细描述我们的项目。
Maven 中的有两大核心:
依赖管理:对 jar 的统一管理(Maven 提供了一个 Maven 的中央仓库mvnrepository.com/ ,当我们添加完对应的依赖Maven就会自动去中央仓库下载相关的依赖,并且解决依赖的依赖问题)。
项目构建:对项目进行编译、测试、打包、部署、上传到私服等。
为什么使用 Maven
由于 Java 的生态非常丰富,无论你想实现什么功能,都能找到对应的工具类,这些工具类都是以 jar 包的形式出现的,而jar 包之间会有关联,在使用一个依赖之前,还需要确定这个依赖所依赖的其他依赖,所以,当项目比较大的时候,依赖管理会变得非常麻烦臃肿,这是 Maven 解决的第一个问题。
Maven 还可以处理多模块项目。简单的项目,单模块分包处理即可,如果项目比较复杂,要做成多模块项目,例如一个电商项目有订单模块、会员模块、商品模块、支付模块…,一般来说,多模块项目,每一个模块无法独立运行,要多个模块合在一起,项目才可以运行,这个时候,借助 Maven 工具,可以实现项目的一键打包。
Maven仓库
仓库类型 | 描述 |
---|---|
本地仓库 | 就是电脑上的本地目录,每台电脑上都一个本地仓库,默认地址:当前用户名\.m2\repository |
私服仓库 | 一般来说都是公司内部搭建的Maven仓库(也称为 二方库),处于局域网中,访问速度快, 这个仓库中存放的jar包一般都是公司内部自己开发或者二次开发封装的jar包 |
中央仓库 | 由Apache团建维护,包含了市面上绝大部分的jar包 |
三个仓库的查找顺序:

实际上,没有特殊需求的话,Maven安装好之后直接就可以用了。一般来说,还是需要稍微配置一下,比如:中央仓库的问题。默认使用 Maven 自己的中央仓库,使用起来网速比较慢,这个时候,可以通过修改配置文件,将仓库改成国内的镜像仓库,国内仓库使用较多的是阿里巴巴的仓库。
1 | <!-- 阿里云仓库 --> |
本地仓库默认位置在 当前用户名\.m2\repository
,这个位置可以自定义,但是不建议大家自定义这个地址,有几个原因:
- 虽然所有的本地的 jar 都放在这个仓库中,但是并不会占用很大的空间。
- 默认的位置比较隐蔽,不容易碰到
技术上来说,当然是可以自定义本地仓库位置的,在 conf/settings.xml
中自定义本地仓库位置:
1 | <localRepository>xxx</localRepository> |
在 cmd 中敲并回车执行:
mvn help:system
首次执行 mvn help:system 命令,Maven会自动帮我们到Maven中央仓库下载缺省的或者Maven中央仓库更新的各种配置文件和类库(jar包)到Maven本地仓库中。
Maven的核心概念
GAV坐标
在平面几何中坐标(x,y)可以标识平面中唯一的一点。在maven中坐标就是为了定位一个唯一确定的jar包。Maven世界拥有大量构建,我们需要找一个用来唯一标识一个构建的统一规范,拥有了统一规范,就可以把查找工作交给机器。
Maven坐标主要组成(GAV) :
【G】groupId:定义当前Maven组织名称;
【A】artifactId:定义实际项目名称;
【V】version:定义当前项目的版本;
GAV坐标的作用:确定一个jar包在互联网上的位置。
依赖管理
scope 依赖范围

其中依赖范围 scope 标签用于控制依赖的范围,即指定依赖的有效范围,以便在不同的环境下进行不同的构建或部署:
- compile: 默认是编译依赖范围。对于编译,测试,运行三种classpath都有效;
- test:测试依赖范围。只对于测试classpath有效;
- provided:已提供依赖范围。对于编译,测试的classpath都有效,但对于运行无效。
- runtime:运行时范围。项目打包运行时才有效,例如:jdbc驱动;
- system:类似 provided,但需要显式地指定依赖的路径或文件。
- import:该 scope 只用于在 pom.xml 中使用 dependencyManagement 元素来管理依赖版本号,不会实际被引入到项目中。
Demo | provided用法
1 | <dependency> |
通常情况下,lombok 依赖的 scope 被定义为 provided。这是因为 lombok 库在编译时需要在 classpath 中存在,但在运行时不需要被打包到部署包中,因为它只是一个编译时工具库(在编译的完成之后,该工具包的作用就已经结束了)。通过编译lombok注解的得到的Class类可以看出来,编译之后对应的lombok的注解都已经被同化为JDK的原生Java代码了,也就是说编译完成之后,使用lombok的注解就可以直接运行了,而不再依赖lombok。因此,将其设置为 provided 可以避免将不必要的 jar 包打包到部署包中,从而减小部署包的大小。同时,在使用 lombok 时,需要将其添加到 IDE 或编译器的插件中,以确保在编译时正常使用 lombok 注解。
Demo | import + <dependencyManagement>
用法
Maven 的 dependencyManagement 元素用于管理项目的依赖版本号,它可以集中管理项目的依赖版本号,避免在多个模块中重复声明版本号,同时方便版本的统一升级。如某版本的spring-boot-starter-parent:
1 | <dependencyManagement> |
自己的项目在使用spring-boot-starter-parent作为parent,同时在需要使用这些依赖的子模块中,不需要再指定依赖的版本号,只需要声明对应的 groupId 和 artifactId。例如:
1 | <dependencies> |
这样,子模块会自动使用父 POM 中声明的版本号来获取依赖。如果需要升级依赖的版本,只需要在父 POM 中修改对应的版本号即可,所有子模块都会自动继承这个版本号。
另外,如果某个子项目需要特定另外的一个版本,只需要添加version标签元素进行声明即可, 会自动覆盖版本 - 就近原则。
注意,dependencyManagement 只是用于管理依赖版本号,它并不会引入实际的依赖。如果需要在子模块中引入依赖,还需要在 dependencies 元素中声明具体的依赖信息。
如果子模块作用域是import:
1 | <dependencies> |
由于 scope 被设置为 import,这些依赖也不会被实际引入到子模块中,而是只用于管理版本号。
依赖传递
如果想进行依赖传递,最好将所有的依赖的范围都设定为默认的compile级别,依赖传递分为两种:
- 直接依赖
- 间接依赖
例如:test2 依赖 test1,test3依赖test2,则test2 直接依赖 test1,test3间接依赖test1;依赖关系图如下:

当第二直接依赖的范围是compile的时候,依赖可以传递;当第二直接依赖的范围是test的时候,依赖不会传递。
依赖冲突
假如test1使用junit4.10依赖,并且scope是compile,那test2、test3都可以使用test1的junit4.10,因为依赖传递下来了;
假如test2又引入了使用junit4.9依赖,那test3会使用junit4.9【就近原则的一个依赖】;
可选依赖
<optional>
属性表示依赖是否可选;也可以理解为是否向下传递。
1 | <optional> true/false<optional> |
在依赖中添加optional选项决定此依赖是否向下传递:
- true:不传递;
- false:传递,默认为false;
排除依赖
用maven管理库依赖,有个好处就是连同库的自身依赖(间接依赖)jar包也全部都一起下载,也就是依赖传递的功能。这种特性免去手工添加的麻烦,但同时也带来了同一个jar包会被下载了不同版本的问题,即jar包的版本冲突。好在pom的配置里面允许用<exclusion>
来排除一些不需要同时下载的依赖jar 。
引入dubbo 依赖:
1 | <dependency> |
但是该包依赖org.springframework 2.5.6.SEC03
的jar包, 但是项目本身又引入了springframework 4.3.3
的jar包,所以这种依赖反倒成了工程瘦身的负担,并可能会出现jar包冲突的情况。
怎么解决这个问题呢?
答案: 使用 exclusions,排除多余的依赖.
1 | <dependency> |
这样,就将引入的 dubbo 2.5.3 中依赖的springframework的依赖排除了。
如果想要去掉全部的依赖,可以使用通配符 *
:
1 | <dependency> |
这样就将dubbo 2.5.3 中所有依赖的jar包都排除了,就是单纯地引入dubbo 2.5.3这一个依赖。
Maven生命周期命令
Maven项目的目录结构
使用Maven创建Web项目,IDE会自动生成一下目录结构:
1 | ProjectName |
Maven生命周期
Maven生命周期就是为了对所有的构建过程进行抽象和统一。包括项目 清理、初始化、编译、打包、测试、部署 等几乎所有构建步骤。
生命周期可以理解为构建工程的步骤。
在Maven中有三套相互独立的生命周期,请注意这里说的是“三套”,而且“相互独立”,这三套生命周期分别是:
- Clean Lifecycle: 在进行真正的构建之前进行一些清理工作;
- Default Lifecycle: 构建的核心部分,编译,测试,打包,部署等等;
- Site Lifecycle: 生成项目报告,站点,发布站点;
Clean 生命周期:清理项目
Clean生命周期一共包含了三个阶段:
- pre-clean:执行一些需要在clean之前完成的工作;
- clean: 移除上一次构建生成的所有文件;
- post-clean:执行一些需要在clean之后立刻完成的工作;
也就是说,mvn clean
等同于 mvn pre-clean clean
两种操作;如果执行 mvn post-clean
,那么 pre-clean
,clean
都会被运行,这是Maven很重要的一个规则,可以大大简化命令行的输入。
Default 生命周期:构造项目
Default生命周期是Maven生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里只解释一些比较重要和常用的阶段:
validate
- generate-sources
- process-sources
- generate-resources
- process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目的源代码。
- process-classes
- generate-test-sources
- process-test-sources
- generate-test-resources
- process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码。
- process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
- prepare-package
package 接受编译好的代码,打包成可发布的格式,如 JAR 。
- pre-integration-test
- integration-test
- post-integration-test
- verify
install 将包安装至本地仓库,以让其它项目依赖。
deploy 将最终的包复制并传输到远程仓库,以让其它开发人员与项目共享
运行任何一个阶段的时候,它前面的所有阶段都会被运行,这也就是为什么我们 运行mvn install
的时候,代码会被编译,测试,打包,安装到本地仓库的原因。此外,Maven的插件机制是完全依赖于Maven的生命周期的,因此理解生命周期至关重要。
Site 生命周期:生成项目站点
这里经常用到的是site阶段和site-deploy阶段,用以生成和发布Maven站点,这可是Maven相当强大的功能,Manager比较喜欢,文档及统计数据自动生成,很方便,很好看。
- pre-site:执行一些需要在生成站点文档之前完成的工作;
- site:生成项目的站点文档;
- post-site:执行一些需要在生成站点文档之后完成的工作,并且为部署做准备;
- site-deploy:将生成的站点文档部署到特定的服务器上;
Maven生命周期命令
使用IDEA创建Maven项目,然后进入项目目录,测试Maven各个命令的作用:
编译:mvn compile
1 | mvn compile |
命令执行完毕后,会生成target目录,该目录中存放了项目编译后的字节码文件。
清除:mvn clean
1 | mvn clean |
命令执行完毕后,会将compile生成的target目录删除。
测试:mvn test
1 | mvn test |
命令执行完毕后,会在target目录中生成三个文件夹:surefire、surefire-reports(测试报告)、test-classes(测试的字节码文件)。
打包:mvn package
1 | mvn package |
命令执行完毕后,会在target目录中生成一个项目文件,为:jar包、war包【web项目】。
安装:mvn install
1 | mvn install |
目的就是将打包好的jar包上传到本地仓库中,执行完毕后,会在本地仓库中出现安装后的jar包,本地的其他工程就可以直接引用了,在使用Maven进行子模块开发的时候最能体现。
部署: mvn deploy
1 | mvn deploy |
目的就是将打包好的jar包部署到指定的远程仓库,比如:团体或者公司的仓库。
Maven也可以使用组合命令,如:
- mvn clean compile
- mvn clean test
- mvn clean package
- mvn clean install
Maven的常用命令
Maven设置版本号命令
对于多module项目,可以使用versions-maven-plugin
的mvn versions:set
命令升级版本号;统一修改pom的版本号,及子模块依赖的版本号:
1 | mvn versions:set -DnewVersion=xxx |
如果有问题,可以回退版本号:
1 | mvn versions:revert |
如果没问题,然后执行如下命令,确认提交版本号:
1 | mvn versions:commit |
Maven依赖树命令
查看项目的依赖模型,可以使用Maven依赖树:
1 | mvn dependency:tree |
Maven的常用标签
Maven的<relativePath/>标签
搭建Maven项目时,子模块指定父模块,经常在<parent>
标签中添加<relativePath/>
标签,如下:
1 | <parent> |
这个<relativePath/>
标签的意思就是parent的路径,具体来说就是从什么地方引用这个parent项目,即这个parent项目的pom在哪里?
- 默认没有
<relativePath/>
标签,那就从默认的路径:../pom.xml,会从本地路径中获取parent的pom文件,建立多个模块时就是在这个情况。 <relativePath/>
,指定了relativePath,但是值是空的,那就始终从仓库中获取,不从本地路径获取。场景的就是使用springboot构建项目。<relativePath>xx<relativePath/>
,指定了一个路径去获取parent的pom文件。
从父级仓库查找依赖版本,MAVEN构建jar包时查找顺序:relativePath元素中的地址 > 本地仓库 > 远程仓库
。
参考文档: