Maven 知识点总结

文章目录

Maven

Maven是一个项目管理工具,它包含了项目对象模型(POM:Project Object Model),项目生命周期(Project Lifecycle),依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)的插件(plugin)以及一组标准集合。

  • 项目对象模型(Project Object Model):一个maven工程都有一个pom.xml文件,通过pom.xml文件定义项目的坐标、项目依赖、项目信息、插件目标等。
  • 项目生命周期(Project Lifecycle):使用maven完成项目的构建,项目构建包括:清理、编译、测试、部署等过程,maven将这些过程规范为一个生命周期。
  • 依赖管理系统(Dependency Management System):通过maven的依赖管理对项目所依赖的jar 包进行统一管理。
  • 插件(plugin):maven 管理项目生命周期过程都是基于插件完成的。
  • 一组标准集合 :maven将整个项目管理过程定义一组标准,比如:通过maven构建工程有标准的目录结构,有标准的生命周期阶段、依赖管理有标准的坐标定义等。

Maven的两个核心作用

  1. 依赖管理:Maven把项目所需要得jar包保存在Maven仓库里面,通过在pom.xml文件中添加所需jar包的坐标,在用到这些jar包时,会根据pom.xml中jar包的坐标把jar包引入进来拿去执行
  2. 项目构建:指的是项目从编译、测试、运行、打包、安装 ,部署整个过程都交给maven进行管理,使用maven一个命令可以轻松完成整个工作,本质上 Maven 是一个插件执行框架,所有的执行过程,都是由一个一个插件独立完成的

1、Maven 坐标

gav,项目中依赖的第三方库以及插件可统称为构件(也可以叫依赖)。每一个构件都可以使用 Maven 坐标唯一标识,也就是说通过 Maven 坐标,我们就可以找到对应的唯一的构件,坐标元素包括:

  • groupId,该 Maven 项目隶属的公司或组织
  • artifactId,当前 Maven 项目的名称,项目的唯一标识符
  • version:定义了当前 Maven 项目所处的版本
  • packaging(可选):定义了 Maven 项目的打包方式(比如 jar,war...),默认使用 jar
  • classifier(可选):常用于区分从同一 POM 构建的具有不同内容的构件,可以是任意的字符串,附加在版本号之后

2、Maven 仓库

本地仓库:settings.xml 文件中可以看到 Maven 的本地仓库路径配置,默认本地仓库路径是在 ${user.home}/.m2/repository。

远程仓库:官方或者其他组织维护的 Maven 仓库,远程仓库可以分为三种:

  • 中央仓库:这个仓库是由 Maven 社区来维护的,里面存放了绝大多数开源软件的包,并且是作为 Maven 的默认配置,但是国内访问较慢,一般我们会通过配置镜像来加速。
  • 私服:私服是一种特殊的远程 Maven 仓库,它是架设在局域网内的仓库服务,私服一般被配置为互联网远程仓库的镜像,供局域网内的 Maven 用户使用,公司一般都会采用私服作为远程仓库。
  • 其他的公共仓库:有一些公共仓库是为了加速访问(比如阿里云 Maven 镜像仓库)或者部分构件不存在于中央仓库中。

Maven 依赖包寻找顺序:先本地仓库 -> 后远程仓库 -> 都没有则报错

3、Maven 依赖

依赖配置

dependencies:一个 pom.xml 文件中只能存在一个这样的标签,是用来管理依赖的总标签。

dependency:包含在 dependencies 标签中,可以有多个,每一个表示项目的一个依赖。

groupId,artifactId,version(必要):依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven 根据坐标才能找到需要的依赖。

type(可选):依赖的类型,对应于项目坐标定义的 packaging。大部分情况下,该元素不必声明,其默认值是 jar。

scope(可选):依赖的作用范围,默认值是 compile。

optional(可选):标记依赖是否可选

exclusions(可选):用来排除传递性依赖,例如 jar 包冲突

依赖范围

classpath 用于指定 .class 文件存放的位置,类加载器会从该路径中加载所需的 .class 文件到内存中。

Maven 编译、测试、运行有三套不同的 classpath:

  • 编译 classpath:编译主代码有效
  • 测试 classpath:编译、运行测试代码有效
  • 运行 classpath:项目运行时有效

Maven 的依赖范围如下:

  • compile:编译依赖范围(默认),使用此依赖范围对于编译、测试、运行三种都有效,即在编译、测试和运行的时候都要使用该依赖 Jar 包。
  • provided:此依赖范围,对于编译和测试有效,而对运行时无效。典型如 lombok。
  • runtime:运行时依赖范围,对于测试和运行有效,但是在编译主代码时无效,典型的就是 JDBC 驱动实现。
  • test:测试依赖范围,从字面意思就可以知道此依赖范围只能用于测试,而在编译和运行项目时无法使用此类依赖,典型的是 JUnit,它只用于编译测试代码和运行测试代码的时候才需要。
  • system:系统依赖范围,使用 system 范围的依赖时必须通过 systemPath 元素显式地指定依赖文件的路径,不依赖 Maven 仓库解析,所以可能会造成构件不可移植。
  • import:将其他模块定义好的 dependencyManagement 导入当前 Maven 项目 pom 的 dependencyManagement 中,不会对三种classpath产生影响。

依赖调解原则

1、对于 Maven 而言,同一个 groupId 同一个 artifactId 下,只能使用一个 version。若相同类型但版本不同的依赖存在于同一个 pom 文件,只会引入后一个声明的依赖

2、由于 Maven 的依赖传递,如果项目的两个依赖同时引入了某个版本不一致的依赖,则会产生冲突

两个原则:最短路径优先 和 声明顺序优先

假设 A->B->C->X(2.0),A->D->X(1.0),则 X(1.0) 会被引用。

假设 A->B->C->X(1.0),A->D->C->X(2.0),如果 B 声明在 D 之前,则X(1.0)被引用,反之。

此时可通过依赖分析,找到有冲突的依赖,比如使用 IDEA插件 Maven Helper 等

排除依赖

如果只依靠 Maven 进行依赖调解,很多情况下是不能满足的,需要手动排除依赖。

还是 A->B->C->X(2.0),A->D->X(1.0) 这个例子,根据最短路径优先原则,我们引入了 X(1.0),但是如果 C 用到了某个 X(1.0) 没有的类或者方法,就会报找不到类/方法的错误(NoSuchMethodError 和 ClassNotFoundException)。

此时,就需要我们通过 exclusion 标签将 X(1.0) 给排除,这样 Maven 就会引用 X(2.0)。

在解决依赖冲突的时候,一般会优先保留版本较高的。这是因为大部分 jar 在升级的时候都会做到向下兼容。

4、Maven 生命周期

Maven 标准生命周期 作用
clean 项目清理(删除 target目录)
default(build) 项目部署
site 项目站点文档创建

我们平时接触最多的就是 default 生命周期,并且七个核心阶段都是顺序执行的。

核心阶段 作用
validate 验证项目是否正确,所有必要信息是否可用(很少单独使用)
compile 编译项目的源代码(将src/main中的java代码编译成class文件,输出到targe目录下)
test 将单元测试的资源文件和代码进行编译,生成的文件位于target/test-classes (打包部署请跳过该阶段)
package 把class文件,resources文件打包成jar包(也可以是war包),生成的jar包位于target目录下
verify 检查包是否有效(很少单独使用)
install 将jar部署到本地仓库,本地的其他模块依赖该jar包时,可以直接从本地仓库去获取
deploy 将jar包部署到远端仓库,需要在maven的setting.xml中配置私服的用户名和密码,以及在pom.xml配置

也就是说,如果我们执行了 mvn package,会从 mvn validate 开始一直执行到 package,其他同理。但是不会执行 mvn clean,因为 clean、site、default 都是独立的生命周期。因此,保险起见,在执行 package、install、deploy 之前先 clean。

5、Maven 聚合与继承

聚合:对于复杂的Maven项目,一般建议采用多模块的方式来设计开发,便于后期维护管理。但是构建项目时,如果每次都需要按模块一个一个进行构建会十分麻烦,而Maven的聚合功能就可以很好的解决这个问题,当用户对聚合模块执行构建任务时,会对所有被其聚合的模块自动地依次进行构建任务。

继承:在一个多模块的项目中,对于同一个依赖的依赖声明要在多个模块的POM都进行声明,会导致有大量重复的依赖声明。所以Maven在设计之时,借鉴了面向对象中的继承思想,可在父模块的POM中声明依赖,子模块的POM文件可通过继承父模块的POM来获得对相关依赖的声明。对于父模块而言,其目的是为了消除子模块的POM文件的重复配置,其不含有任何实际的项目代码,所以父模块POM文件的packaging元素同样需要设置为pom。

参考文章&推荐阅读

Maven -- 构建生命周期简介 (apache.org)

高效使用Java构建工具|Maven篇 (qq.com)

maven学习笔记(超详细总结) - clear_love8 - 博客园 (cnblogs.com)

相关推荐
赛丽曼7 分钟前
Python中的TCP
python
小白~小黑8 分钟前
软件测试基础二十(接口测试 Postman)
python·自动化·postman
codists9 分钟前
《Django 5 By Example》阅读笔记:p76-p104
python·django·编程人
欧阳枫落17 分钟前
python 2小时学会八股文-数据结构
开发语言·数据结构·python
天天要nx21 分钟前
D64【python 接口自动化学习】- python基础之数据库
数据库·python
feifeikon1 小时前
Python Day5 进阶语法(列表表达式/三元/断言/with-as/异常捕获/字符串方法/lambda函数
开发语言·python
杰仔正在努力2 小时前
python成长技能之枚举类
开发语言·python
Eiceblue2 小时前
通过Python 调整Excel行高、列宽
开发语言·vscode·python·pycharm·excel
Jam-Young2 小时前
Python中的面向对象编程,类,对象,封装,继承,多态
开发语言·python
Light602 小时前
低代码牵手 AI 接口:开启智能化开发新征程
人工智能·python·深度学习·低代码·链表·线性回归