Maven仓库
什么是 Maven 仓库?
在 Maven 的术语中,仓库是一个位置(place),例如目录,可以存储所有的工程 jar 文件、library jar 文件、插件或任何其他的工程指定的文件。
Maven 仓库有三种类型:
- 本地(local)
- 中央(central)
- 远程(remote)
1.本地仓库
Maven 本地仓库是机器上的一个文件夹。它在你第一次运行任何 maven 命令的时候创建。
Maven 本地仓库保存你的工程的所有依赖(library jar、plugin jar 等)。当你运行一次 Maven 构建,Maven 会自动下载所有依赖的 jar 文件到本地仓库中。它避免了每次构建时都引用存放在远程机器上的依赖文件。
默认位置:
C:\Users\用户名\.m2\repository
更改默认位置:
修改Maven目录下的conf文件夹下的settings.xml
在settings中增加一条
<localRepository>C:/MyLocalRepository</localRepository>
下次Maven将会把依赖下载到这个文件夹下
2.中央仓库
Maven 中央仓库是由 Maven 社区提供的仓库,其中包含了大量常用的库。
中央仓库的关键概念:
- 这个仓库由 Maven 社区管理。
- 需要通过网络才能访问。
要浏览中央仓库的内容,maven 社区提供了一个 URL:http://search.maven.org/#browse。使用这个仓库,开发人员可以搜索所有可以获取的代码库。
3.远程仓库
如果 Maven 在中央仓库中也找不到依赖的库文件,它会停止构建过程并输出错误信息到控制台。为避免这种情况,Maven 提供了远程仓库的概念,它是开发人员自己定制仓库,包含了所需要的代码库或者其他工程中用到的 jar 文件。
举例说明,使用下面的 POM.xml,Maven 将从远程仓库中下载该 pom.xml 中声明的所依赖的(在中央仓库中获取不到的)文件。
<repositories>
<repository>
<id>companyname.lib1</id>
<url>http://download.companyname.org/maven2/lib1</url>
</repository>
</repositories>
Maven 依赖搜索顺序
当我们执行 Maven 构建命令时,Maven 开始按照以下顺序查找依赖的库:
- 步骤 1 - 在本地仓库中搜索,如果找不到,执行步骤 2,如果找到了则执行其他操作。
- 步骤 2 - 在中央仓库中搜索,如果找不到,并且有一个或多个远程仓库已经设置,则执行步骤 4,如果找到了则下载到本地仓库中已被将来引用。
- 步骤 3 - 如果远程仓库没有被设置,Maven 将简单的停滞处理并抛出错误(无法找到依赖的文件)。
- 步骤 4 - 在一个或多个远程仓库中搜索依赖的文件,如果找到则下载到本地仓库已被将来引用,否则 Maven 将停止处理并抛出错误(无法找到依赖的文件)。
POM的例子
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
要注意的是,每个项目只有一个POM文件。
- 所有的 POM 文件要项目元素必须有三个必填字段: groupId,artifactId,version
- pom.xml 的根元素是 project,它有三个主要的子节点。
节点 | 描述 |
groupId | 这是项目组的编号,这在组织或项目中通常是独一无二的。 |
artifactId | 这是项目的ID。这通常是项目的名称。 |
version | 这是项目的版本。与groupId一起使用 |
生命周期
maven把项目的构建划分为不同的生命周期(lifecycle)。粗略一点的话,它这个过程(phase)包括:
- 验证(validate):验证项目是否正确
- 编译(compile): 编译项目的源代码
- 测试(test):使用合适的单元测试框架测试编译的源代码。这些测试不应该要求代码被打包或部署
- 打包(package)
- 验证(verify)
- 安装(install)
- 部署(deploy)
maven中所有的执行动作(goal)都需要指明自己在这个过程中的执行位置,然后maven执行的时候,就依照过程的发展依次调用这些goal进行各种处理。
这个也是maven的一个基本调度机制。一般来说,位置稍后的过程都会依赖于之前的过程。当然,maven同样提供了配置文件,可以依照用户要求,跳过某些阶段。
Maven项目的属性
maven使用如下几个要素来唯一定位某一个输出物: groupId:artifactId:packaging:version 。比如org.springframework:spring:2.5 。每个部分的解释如下:
- groupId 公司,团体等。如Apache Software的项目有以org.apache开头的groupId。
- artifactId 项目的唯一标识符。比如一个helloworld项目就叫helloworld。
- packaging 项目打包输出的类型,默认是jar。类型为war的项目产生一个web应用。
- version 一个项目的特定版本。发布的项目有一个固定的版本标识来指向该项目的某一个特定的版本。而正在开发中的项目可以用一个特殊的标识,这种标识给版本加上一个"SNAPSHOT"的标记。
maven在版本管理时候可以使用几个特殊的字符串 SNAPSHOT ,LATEST ,RELEASE 。比如"1.0-SNAPSHOT"。各个部分的含义和处理逻辑如下说明:
- SNAPSHOT:如果一个版本包含字符串"SNAPSHOT",Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为UTC时间。例如,"1.0-SNAPSHOT"会在2010年5月5日下午2点10分发布时候变成1.0-20100505-141000-1。这个词只能用于开发过程中,因为一般来说,项目组都会频繁发布一些版本,最后实际发布的时候,会在这些snapshot版本中寻找一个稳定的,用于正式发布,比如1.4版本发布之前,就会有一系列的1.4-SNAPSHOT,而实际发布的1.4,也是从中拿出来的一个稳定版。
- LATEST:指某个特定构件的最新发布,这个发布可能是一个发布版,也可能是一个snapshot版,具体看哪个时间最后。
- RELEASE:指最后一个发布版。
Maven 项目依赖
1.多模块依赖和继承
项目结构图:
parent
├─childA(model层)
│ └─pom.xml(jar)
├─childB(web层)
│ └─pom.xml(war)
└─pom.xml(pom)
- parent中执行mvn install就能将 childA和childB 一起编译
parent的pom.xml做如下配置:
<groupId>com.parent</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging> <!-- pom表示它是一个被继承的模块 -->
<modules>
<module>childA</module> <!-- 不加module则不会被联合编译 -->
<module>childB</module>
</modules>
childA和childB的pom.xml都需要配置parent,防止引入的包冲突(如果不加parent,会分别去编译他们引入的依赖,会重复引入包):
childA 的 pom.xml
<parent>
<artifactId>parent</artifactId>
<groupId>com.parent</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>childA</artifactId>
<packaging>jar</packaging>
childB 的 pom.xml
<parent>
<artifactId>parent</artifactId>
<groupId>com.parent</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>childB</artifactId>
<packaging>war</packaging>
2.子pom间存在引用关系,比如childB引用到了childA的jar包
<dependency>
<groupId>com.module</groupId>
<artifactId>childA</artifactId> <!--加上childA的依赖-->
<version>1.0-SNAPSHOT</version>
</dependency>
3.子项目继承父项目的依赖
parent中加上<dependencyManagement>,child项目就可以继承parent项目的依赖,并且在child中可以不用加version了。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
</dependencyManagement>
依赖范围
依赖范围通过<scope>属性配置
如果不填,默认为complie
scope 属性包括:
- compile(编译范围):编译范围的<dependency>在所有的classpath中可用,同时它们也会被打包
- provided(已提供范围):表示部署的环境当中有某容器已经提供了该jar包,只在编译classpath(不是运行时)可用。它们不是传递性的,也不会被打包。例如,如果你开发了一个web应用,你可能在编译classpath中需要可用的Servlet API来编译一个servlet,但是你不会想要在打包好的WAR中包含这个Servlet API;这个Servlet API JAR由你的servlet容器(Tomcat)提供。
- runtime(运行时范围):只在运行和测试系统的时候需要,但在编译的时候不需要。
- test(测试范围):在一般的 编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。
- system (系统范围):该范围不推荐使用(你应该一直尽量去从公共或定制的Maven仓库中引用依赖)
依赖冲突
加入同一个项目同时继承两个不同的项目,而这两个项目中有同一个依赖但是版本不同,这时候该怎么处理呢?Maven提供了两种方法
1.短路径优先
- A->B->C(1.0)
- A->D(2.0)
这里因为A->D具体更短,所以A采用D(2.0)的版本
2.先声明先优先
- A->B(1.0)
- A->C(2.0)
这时候距离相同,但是因为A->B先声明,所以A采用B(1.0)的版本
常用命令
- Compile 编译
- Test 测试
- Package 打包
- Clean 删除target
- Install 安装jar包到本地仓库中
Maven默认插件
不配置Plugin时,Maven默认会使用以下插件。如果针对各个 plugin 有特殊配置的话,需要显示指定 plugin 和 属性配置。
plugin | function | life cycle phase |
maven-clean-plugin | 清理上一次执行创建的target文件 | clean |
maven-resources-plugin | 处理资源文件 | resources,testResources |
maven-compiler-plugin | 编译Java代码 | compile、testCompile |
maven-surefire-plugin | 执行单元测试文件 | test |
maven-jar-plugin | 创建 jar | package |
maven-install-plugin | 拷贝jar到本地的maven仓库 .m2/repository 下面 | install |
maven-deploy-plugin | 发布 jar | deploy |
maven-site-plugin | 生成文档 | site |
参考:
https://segmentfault.com/a/1190000015077021
https://www.imooc.com/learn/443