在软件设计领域,Duplication is root of all evil【重复是万恶之源】。 我们一直在不停地寻找避免重复的方法。设计的重复、编码的重复、文档的重复,当然也有项目构建的重复。 Maven可以最大化消除项目构建的重复。构建?编译、跑UT、生成文档、打包和部署 Maven的核心作用是编译、测试、打包,它抽象了构建生命周期,并且为绝大部分的构建任务提供了已实现的插件,我们不再需要定义过程,甚至不需要再去实现这些过程中的一些任务。 最简单的例子就是测试,我们没必要告诉Maven去测试,更不需要告诉 Maven如何运行测试,只需要遵循Maven的约定编写好测试用例,当我们运行构建的时候,这些测试便会自动运行。
记住这个词“约定”。 约定优于配置(Convention Over Configuration)是Maven的设计哲学,也是业界一个很流行的原则。
Maven是一组标准集合,一个依赖管理系统,一个跨平台的项目管理工具,一个Apache组织中颇为成功的开源项目。
Maven 使用项目对象模型 (POM) 和一组插件来构建项目。 Maven主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。无论是小型的开源类库项目,还是大型的企业级应用;无论是传统的瀑布式开发,还是流行的敏捷模式,Maven都能大显身手。
有了Maven我们可以自动化构建过程,从清理、编译、测试到生成报告,再到打包和部署。我们不需要也不应该一遍又一遍地输入命令,一次又一次地点击鼠标,我们要使用的是使用Maven配置好项目,然后输入简单的命令(如mvn clean insatll),Maven会帮我们处理那些烦琐的任务。
Maven的核心功能是合理描述项目间的依赖关系,通俗点就是通过pom.xml文件的配置获取jar包不用手动的去添加jar包,这个pom.xml后面会展开聊,不过已经学习过maven的 人应该对这个很熟悉。 其本质就是通过配置pom.xml来获取jar包,当然这是在该项目必须是maven项目的前提下。
Maven 是意第绪语,意思是“知识的积累”,可以翻译为“专家”或“内行”。
此外,Maven是跨平台的,这意味着无论是在Windows上,还是在Linux或者Mac上,都可以使用同样的命令。这个特性也消除了Maven在不同平台的重复。
那么,什么是maven项目?使用Maven进行项目管理的项目就是Maven项目。
见文末
我们这样来理解maven项目,就是在java项目和web项目上裹了一层maven,本质上java项目还是java项目,web项目还是web项目,但是包裹了maven之后,就可以使用maven提供的一些功能来更高效得管理构件的依赖,即可以通过pom.xml声明式管理jar,譬如添加、移除jar。也就是说你通过更新pom.xml中的内容,项目依赖的jar就会出现变化,不再需要把jar包copy进来或删除。
就像通过网约车平台打车一样,只需要在手机上操作几个就可打到车,还是打车这个事,有了网约车平台,这样用户打车这个事就简单、高效多了。不再需要在路边等车、拦车了。
Maven的一大功能是管理项目依赖。为了能自动化地解析任何一个Java构件,Maven就必须将它们唯一标识,这种唯一性依赖构件管理的底层基础---坐标。 关于坐标(Coordinate),大家最熟悉的定义应该来自于平台几何。在一个平面坐标系中,坐标(x,y)表示该平面上与x轴距离为y,与y轴距离为x的一个点,任何一个坐标都能够唯一标识该平台中的一个点。
在实际生活中,我们也可以将地址看成是一种坐标。省、市、区、街道等一系列信息同样可以唯一标识城市中的任一居住地址和工作地址。邮局和快递公司正是基于这样一种坐标进行日常工作的。
对应于平面中的点和城市中的地址,Maven的世界中拥有数量非常巨大构件,也就是平时用的一些jar、war等文件。 Maven提供了这样一个解决方案:世界上任何一个构件都可以使用Maven坐标唯一标识 ,Maven坐标的元素包括groupId、artifactId、version、packaging、classifier。现在,只要我们提供正确的坐标元素,Maven就能找到对应的构件。
平时讲的Maven GAV就是指groupId、artifactId、version。
在Maven世界中,任何一个依赖、插件或者项目构建的输出,都可以称为构件。Maven坐标为各种构件引入了秩序,秩序的建立才是管理的开始。当我们开发的项目是基于Maven进行项目管理,这个项目也需要为其定义适当的坐标,这是Maven强制要求的。在这个基础上,其它Maven项目才能引用该项目生成的构件。
Maven仓库是一个统一存储和管理Maven构件的地方。通俗的讲,Maven仓库就是存放jar包的地方,即我们前面说的通过pom.xml中通过设置索引来到仓库中寻找jar包。
任何一个基于Maven进行项目管理的项目使用的构件,都依赖Maven仓库。 在Maven的世界里,任何一个依赖、插件或者项目构建的输出,都可以称为构件。而构件的物理表示方式是文件,得益于Maven的坐标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。
从职责划分的角度看, 从依赖管理的角度看,Maven仓库是软件项目依赖的第三方库,这个库所在的位置叫做仓库。
Maven仓库就是存放jar的地方,这些Jar在仓库中按约定的规则存在。使用Maven客户端工具可以根据Maven坐标高效地找到。
根据访问时是否需要通过网络来访问,Maven仓库可以分为两类:
Maven的布局负责将构件坐标转换为通用路径并且确保构件在本地或远程都可以被唯一定位,这些路径随后用于构建某些URI(文件路径、URL等,取决于上下文)。 显然,自2002年Maven诞生以来,布局也不断发展。为了简化起见,我们将介绍当前的布局(即“maven2”或“默认”),因为自Maven 3.x版本发布以来,不再支持已弃用的“Maven1布局”(即“遗留”)。
转换规则实际上相当简单:
考虑以下构件属性:
groupId: 这是项目组织的唯一标识符,通常是公司或组织的域名反转形式,例如 org.apache.maven。 artifactId: 这是项目在组织中的唯一基础名称,例如 maven-core。 version: 这是项目的当前版本,例如 3.8.1。 packaging: 这是构建的输出类型,例如 jar、war、pom 等。 classifier: 这是构建输出的一个额外标识符,用于表示同一项目的替代版本,例如 sources、javadoc 等。 extension: 这是文件扩展名,通常是打包类型的后缀,例如 .jar、.war 等。
Maven 布局将根据上述属性生成构件的最终路径。对于本地仓库和远程仓库,路径的格式略有不同。
对于本地仓库(默认位于用户主目录下的 .m2/repository 目录),构件的路径通常遵循以下格式:${basedir}/${groupId}/${artifactId}/${version}/${artifactId}-${version}-${classifier}.${extension} 其中 ${basedir} 是本地仓库的基础目录。 对于远程仓库,构件的URI通常遵循以下格式: http://[host]:[port]/[context]/${groupId}/${artifactId}/${version}/${artifactId}-${version}-${classifier}.${extension} 这里的 [host], [port], 和 [context] 取决于远程仓库的配置。
这些规则确保了无论在本地还是远程,构件都可以根据其属性被唯一定位。
仓库镜像有两层含义: 1、一个远程仓库的镜像 2、提供一个方便地切换远程仓库地址的途径。需要在setting.xml中配置mirror标签。
一个远程仓库的镜像
如果远程仓库A包含了远程仓库B中的所有资源,则仓库A可以称为仓库B的镜像或映射。
镜像就是一个远程仓库的一个映射仓库,在国内来说,意义上相当于代理的作用。比如中央仓库因为服务器在国外,又因为某些不可描述的原因,所以国内开发人员从中央仓库中下载的依赖包会因为各种各样的网络因素导致错误则下载十分缓慢。所以这时候镜像的作用就显现出来了。 可以说,镜像就是国外公共仓库的网络加速器,也可以说镜像是国外仓库在国内的版本。
一个方便切换远程仓库地址的途径
当maven需要到的依赖jar包不在本地仓库时, 就需要到远程仓库下载 。mirrors标签的配置可以按规则拦截对远程仓库的请求 ,如果命中规则,则会改变目标仓库的地址。
要配置一个给定仓库的镜像,您需要在设置文件(${user.home}/.m2/settings.xml)中提供新的仓库ID和URL,并指定mirrorOf设置,该设置是您正在使用镜像的仓库的ID。 setting.xml文件中的mirror标签用于定义仓库镜像,其相当于一个拦截器。当 mirror 的 mirrorOf 值与 repository 的 id 相同时,repository 定义的仓库会被拦截,转而使用 mirror 中定义的仓库地址。 配置范例如下:
<!-- 以下<mirror>的含义是:项目中所有向central这个仓库发的请求,都会转发到http://maven.aliyun.com/nexus/content/groups/public/而不是https://repo.maven.apache.org/maven2了。-->
<mirrors>
<mirror>
<!-- id表示该镜像的唯一标识符, 用来区分不同的mirror元素,
同时会使用server中id相同的授权配置链接到镜像,也就是说如果该镜像需要认证,
则需要配置一个id为aliyun的<server>即可 -->
<id>aliyun</id>
<name>aliyun maven</name>
<!-- 被镜像的仓库的id, 必须与repository节点设置的 ID 一致。
| mirrorOf 的配置语法:
| central = 表示该镜像为中央仓库的镜像,任何对于中央仓库的请求都会转至该镜像(http://maven.aliyun.com/nexus/content/groups/public/)。
| * = 匹配所有远程仓库,这样的话所有在pom中定义的仓库都不生效了。
| external:* = 匹配除localhost、使用file://协议外的所有远程仓库。
| repo1,repo2 = 匹配仓库repo1和repo2。
| *,!repo1 = 匹配所有远程仓库, repo1 除外。
|-->
<mirrorOf>central</mirrorOf>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
</mirrors>
如上图配置所示,Maven 会用 http://maven.aliyun.com/nexus/content/groups/public/ 这个仓库镜像替换 Maven 中央仓库,其中 central 是 Maven 中央仓库的 ID 标识。镜像的原理是,当你为中央仓库添加了镜像,Maven访问中央仓库的时候就自动切换到镜像的url。我们经常说用阿里云的 Maven 仓库可以提速,其实就是使用这种方法实现的。
远程仓库的网络加速器,一般是出于网速考虑。多个镜像按照id字母顺序进行搜索排列(即与编写的顺序无关)。 如果不配置镜像时,默认使用Maven的中央仓库。
谨慎配置 mirrorOf 为 *
如果mirrorOf的值是*,则是为所有的远程仓库都添加该镜像,此时所有的repository配置都失效了。这在企业内部使用Maven私服场景,特别有用,把mirrorOf配置为*,url配置为私服的地址,这样在访问远程Maven仓库时,都会自动切换到私服。 配置范例如下:
<mirrors>
<mirror>
<id>nexus</id>
<!--私服URL-->
<url>http://192.108.1.100/nexus/content/groups/public/</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
使用mirror的一些原因: 1、加速组织内Maven项目的构建速度。更方便使用互联网上有一个地理上更近且速度更快的仓库。 2、你希望用自己拥有更大控制权的内部仓库来替换特定的仓库。 3、你想要运行一个仓库管理器,为镜像提供本地缓存,并需要使用其URL。
通过配置settings.xml中的mirrors标签,可以让组织内部的Maven客户端先从局域网的私服下载依赖,局域网中内网络质量和网络拓扑可控,正常情况下比互联网上的要好一些,这样就提升了依赖的下载速度,这样一来Maven项目构建速度就提升了。Maven项目依赖越多这种提升速度就越明显。
没有配置mirror标签
配置了mirror标签
mirrorOf配置规则的含义:如果访问A仓库,则将请求切换到B仓库。
mirror标签的优先级高于repository标签。 pom.xml 和 setting.xml 中配置的仓库和镜像优先级关系: mirror(setting.xml)> repository(pom.xml) > repository(setting.xml) 如果有mirrors标签配置了 mirrorOf = *, 则不管项目的 pom.xml 配置了什么仓库, 最终都会被切换到镜像仓库。
Maven下载jar时的检索顺序:本地仓库->中央仓库->其他远程仓库
先从本地仓库中寻找,没有找到再到中央仓库寻找,然后再到其他远程仓库寻找
仓库顺序:远程仓库URL在查询构件时按照以下顺序进行,直到有一个返回有效结果。
有效的settings.xml文件: 1、全局settings.xml 2、用户settings.xml
本地有效的项目POM: 3、本地pom.xml 4、父POMs,逐层查找 5、Super POM 为什么全局settings.xml中配置的repository标签优先级最高? 站在Maven架构师的角度考虑一下:Maven项目依赖的第三方jar都是全网统一的、没有个性化。 maven官方仓库中的jar相对而言更全面、质量更高、更可以信赖,所以先来这找一下,如果找到就结束了。并且90%以下的jar都可以在这找到。
从构件到有效POM的依赖路径。 对于这些位置中的每个位置,都会首先按照 构建配置文件简介 中的顺序查询配置文件内的仓库。 https://maven.apache.org/guides/introduction/introduction-to-profiles.html
在从仓库下载之前,会应用mirror配置。
有效的设置和本地构建POM(考虑了配置文件),可以很容易地通过mvn help:effective-settings和mvn help:effective-pom -Dverbose查看它们的仓库顺序。
项目依赖的管理是指maven通过依赖传播、依赖优先原则、可选依赖、排除依赖、依赖范围等特性来管理项目ClassPath。
1、依赖的传播特性
我们的项目通常需要依赖第三方组件,而第三方组件又会依赖其它组件。遇到这种情况Maven会将依赖网络中的所有节点都会加入ClassPath当中,这就是Maven的依赖传播特性。
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.19</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
上图中,项目pom.xml写了mysql-connector-j,这种情况项目对mysql-connector-j的依赖叫直接依赖,而对protobuf-java依赖是通过mysql-connector-j传递的所以叫间接依赖。
2、依赖的优先原则
基于依赖传播特性,导致Maven项目中整个依赖网络很复杂,必然会出现相同组件不同版本的情况。面对这种组件版本冲突,Maven会基于以下依赖优先原则选择其中一个版本。
第一原则:最短路径优先。 如A->B->C->D1 , A->E->D2 , 那么最终项目A依赖的D的版本是D2。
第二原则:相同路径下配置在前的优先。 如A->B->D1 , A->C->D2 ,如果项目A pom.xml中B依赖写在C依赖之前,那么最终项目A依赖的D的版本是D1。
3、依赖传递原则
optional :是否启用依赖传递,默认false表示需要依赖传递。 如果项目D依赖项目C,C依赖A,默认情况下D中会有A的依赖,如果在项目C的pom.xml中配置项目A的dependency的optional为true,则D中不会有A的依赖。 如下所示:
4、排除依赖
有时候为了解决项目依赖冲突,需要排除依赖的jar包通过Maven依赖传递特性引用的其他jar。 即排除指定的间接依赖。通过配置 <exclusions>标签来排除指定组件/构件。
5、依赖的范围(scope)
Maven中依赖范围主要在项目部署阶段生效。 scope标签的作用:控制dependency元素的使用范围,通俗的讲就是控制jar包在哪些阶段被加载和使用。 scope定义了类包在项目的使用阶段。项目阶段包括:编译,运行,测试和发布。
譬如junit这个组件,我们只有在运行测试用例的时候去要用到,这就没有必要在打包的时候把junit.jar包过构建进去。
这种需求,可以通过Maven 的依赖范围配置<scope>来达到这种目的。 maven 总共支持以下四种依赖范围:
不建议使用system。因与本地环境耦合度高,引入系统classpath或非maven远程仓库收录的第三方Jar时,推荐通过install到私服的方式解决。 install jar到私服的命令如下所求:
mvn install:install-file -DgroupId=dm.jdbc -DartifactId=DmJdbcDriver -Dversion=1.8 -Dpackaging=jar -Dfile=D:/DmJdbcDriver18.jar
当scope为system的时候打包不会自动打包进去的,所以要添加一个参数才能打包进去的
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
在日常的开发过程中,可以借助MavenHelper插件实现对项目依赖的管理。 以IntelliJ IDEA IDE为例:
Maven的安装及配置 mac: brew install maven 其它平台:先下载、再安装、后配置环境变量 唐成,公众号:的数字化之路AI|Mac换机+ChatGPT=!!!
Maven Repositories https://maven.apache.org/repositories/index.html
Introduction to Repositories https://maven.apache.org/guides/introduction/introduction-to-repositories.html
《Maven 实战》许晓斌 Maven是什么?Maven的概念+作用+仓库的介绍+常用命令https://cloud.tencent.com/developer/article/1705945 https://maven.org.cn/what-is-maven.html
MAVEN中镜像和仓库的区别 https://blog.csdn.net/goodjava2007/article/details/123049404
镜像(mirror)和仓库(repository)的区别 https://www.jianshu.com/p/0c57535d1da8
Maven的Scope区别笔记 https://www.cnblogs.com/softidea/p/11468170.html
导入maven仓库中没有的jar包 https://zhuanlan.zhihu.com/p/375454726
Using Mirrors for Repositories https://maven.apache.org/guides/mini/guide-mirror-settings.html
Setting up Multiple Repositories https://maven.apache.org/guides/mini/guide-multiple-repositories.html
maven多个仓库查询的优先级顺序 https://juejin.cn/post/7224017824556761148
Maven配置多仓库无效? https://juejin.cn/post/6963471281962352671