03_Maven核心_POM_坐标与依赖
POM(Project Object Model,项目对象模型)文件定义了项目的基础信息,用于描述项目如何构建,声明项目依赖等等。
打开新建项目的POM文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bjsxt</groupId> <artifactId>myblog</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>myblog</name> <description>test maven hello word</description> </project>
第一行指定XML文档的版本和编码方式。
第二行指定了POM模型的版本,Maven2和Maven3都是4.0.0
后面三行就比较重要了,它定义了项目的基本坐标。
第六行说明了项目的打包方式,是一个war表示这个项目是个web项目
PS:这里还可以是jar,表示一个普通项目(如果没有packaging,那么默认就是jar)
还有POM类型,这个可以用来聚合或者继承(后面会介绍)
一、坐标介绍:
我们知道在平面中,只要给点X,Y的坐标,就可以在平面中确定唯一个点。在Maven中坐标的含义类似。即:给定groupId、artifactId、version那么就能唯一确定一个构件。
名词定义--构件:可以理解为一个项目,一个jar包,一个war包等等。
为什么需要坐标呢?
我们知道在这个世界上有海量的构件,如果没有一个唯一的标识,Maven将很难定位。为了解决这个问题,Maven定义一个规定:世界上任何一个构件都可以在Maven中找到唯一一个标识,Maven的坐标包括:groupId,artifactId,version组成。只有我们提供正确的坐标。Maven就能定位唯一构件。
坐标解释:
groupId:定义了项目属于那个组,这个组一般和公司组织相关联。例如:Apache基金组织的groupId就可以是org.apache(一般采用公司域名)
artifactId:定义当前Maven项目在组中的唯一ID。例如Apache基金下面的hadoop组织可以定义为hadoop。描述方式:org.apache:hadoop
version:指定当前项目的版本。上面的版本是0.0.1-SNAPSHOT。是一个快照版本。
版本格式:<主版本>.<次版本>.<增量版本>-<限定版本>
什么叫做SNAPSHOT(快照)版本?
项目在开发过程中是不停在更新的,这时我们的版本可以设置为快照版本。下面举个例子描述一下:
小明和小红协同开发,小红开发的B模块是依赖小明开发的A模块。如果小明更新了A模块小红该怎么做呢?
方法一:小红可以将A模块的源码拿来自己编译,但是这种方式太二了。如果项目编译报个错,小红就傻了,她得找小明帮她解决。
方法二:小明每次更新了A模块就更新A模块的版本号,那么小红可以直接更新依赖就可以得到A模块最新的版本,但是问题来了,其实每次A只是更新了一点点,这会造成版本泛滥
方法三:利用快照版本。如果小明在开发A模块时使用的是快照版本,那么小红就不用管这个依赖了,因为Maven会帮我们定时去更新最新版本的A模块。这个时间可以在updatepolicy中来设置
快照版本的实现机制?
如果一个版本包含字符串“SNAPSHOT”,Maven 就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为 UTC(协调世界时)。
例如,如果你的项目有个版本为“1.0-SNAPSHOT”并且你将这个项目的构件部署到了一个 Maven 仓库,如果你在UTC2008 年 2月 7号下午 11:08 部署了这个版本,Maven 就会将这个版本展开成“1.0-20080207-230803-1”。换句话说,当你发布一个snapshot,你没有发布一个软件模块,你只是发布了一个特定时间的快照版本。
PS:还有两个版本我们可以认识一下latest版本和release版本
LATEST 是指某个特定构件最新的发布版或者快照版(snapshot),最近被部署到某个特定仓库的构件。(最新版)
RELEASE 是指仓库中最后的一个非快照版本。(发行版)
二、依赖
我们在pom.xml中添加一段依赖代码
<dependencies> <dependency> <groupId>ins</groupId> <artifactId>arch4</artifactId> <version>4.2.9</version> <exclusions>...</exclusions> <optional>...</optional> <scope>...</scope> <type>...</type> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
上面这段代码表示设置了一个依赖,即在我们项目中添加了一个构件(jar包)。前面已经说过,Maven通过坐标唯一定义个构件。所以在配置依赖时,只要提供groupId,artifactId,version就可以定位一个构件
groupId,artifactId,version:设置依赖最重要的元素定位一个构件
type:依赖类型,对应项目坐标中的packaging
scope:依赖范围
optional:标记依赖是否可选
exclusions:用来排除传递性依赖
依赖范围
Maven有以下几种依赖范围:
compile:编译依赖范围(默认类型),在编译、测试、运行都起作用的依赖
test:测试依赖范围,在测试起作用的依赖
provided:已提供依赖范围,对于编译和测试classpath有效,但运行时无效。典型的例子Servlet-api,在编译和测试时需要,在运行时有容器提供
runtime:运行时依赖范围,在测试和运行classpath有效,编译时无效。典型的例子是JDBC驱动实现,项目编译时只提供JDBC接口,只有执行测试或者运行时才需要实现JDBC接口
system:略
import:导入依赖范围
上面描述中提到了编译、测试、运行classpath,这是什么意思呢?
Maven在编译项目时需要一套classpath,即:支持项目编译成功的一些jar包。Maven在编译和测试时也需要一套classpath。最典型的例子是junit测试,junit测试在测试阶段需要junit提供测试需要的依赖,但在运行时可以去掉这块的依赖。同样,Maven在运行时也需要一套classpath。依赖范围的控制就是:控制依赖与3中classpath的关系
传递依赖
如果A依赖B,B又依赖C。那么A对C来说就是传递依赖。Maven这种传递依赖为我们减少了很多负担。就用上面的简单例子:A依赖B,B依赖C。我们只需要在pom.xml中添加B的依赖。那么Maven自动帮我们附加了对C的依赖。想像一下,在以前我们开发web项目时,将所有依赖的jar考到lib下面。你还要考虑B依赖的jar包有哪些,这些jar包还不能缺了。现在就不用管这些问题了。
传递依赖和依赖范围的关系
问题描述:
A依赖B,B依赖C。
A对B是第一直接依赖
B对C是第二直接依赖
A对C是传递依赖
第一直接依赖的范围和第二直接依赖的范围决定了传递依赖的范围
X轴表示第二直接依赖范围
Y轴表示第一直接依赖范围
X轴和Y轴的交点就表示传递依赖的范围
依赖冲突
有下面这种情况:
项目A有这样的依赖关系:A-->B-->C-->Z(1.0);A-->D-->Z(2.0)这样就造成了Z在项目中出现两个版本。Maven是怎么处理这种问题的呢?
第一原则:路劲最近者优先,其中Z(1.0)路径为3。Z(2.0)路径为2 所以选择Z(2.0)
第二原则:如果路径相同,那么使用第二原则,谁在pom中先被声明就用谁