Maven 是一个项目管理工具,它最主要的两个功能就是:依赖管理和项目构建。

何为依赖管理

​ 在传统项目中,我们的项目如果需要第三方提供的库就必须得去官网上下载,有了Maven我们只需要在pom文件中配置对应库的坐标,Maven则会自动的去中央仓库下载对应的第三方库,这就是Maven的依赖管理。

何为项目构建

​ 依赖管理可能比较好理解,那到底什么是项目构建呢?

​ 在工作中,除了需要编写源代码以外,我们每天有相当一部分时间花在了项目编译、运行单元测试、生成文档、打包和部署等繁琐且不起眼的工作上,这就是构建。如果我们还去手工的完成这一部分工作,那么效率就太低了,Maven的出现使得这一整套的动作向一条流水线一样,通过一个简单的命令就能完成。

Maven的核心思想

约定优于配置(convention over configuration),Maven通过超级pom约定了很多通用的配置(例如:目录结构),这样做省去了我们繁琐的配置,我们只需要按照Maven的约定进行项目的创建即可。

超级POM的位置

${Maven_Home}/lib/maven-model-builder-3.3.9.jar/org/apache/maven/model/pom4.0.0.xml

{% asset_img 13.png %}

上面截取的是超级POM的一部分,它定义了Maven项目的目录结构、编译后的输出目录、资源目录等。

Maven的目录结构

坐标和依赖

何为坐标

在一个平面中只要给定一个直角坐标系,所有的点都能通过一个(x,y)来表示;同样的Maven也需要定义一种坐标形式来定位众多的构件。

项目坐标定义

任何一个构件都必须明确定义自己的坐标,而坐标是通过以下元素来定义的:groupId、artifactId、version、packaging、classfier。

  • groupId:定义当前Maven项目隶属的实际项目。首先Maven项目和实际项目并不是一一对应的,因为有些大的项目会分成很多模块,例如SpringFramework项目(实际项目)被分成spring-core、spring-beans、spring-context等模块。

    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.1.9.RELEASE</version>
    <packaging>jar</packaging>
  • artifactId:该元素定义实际项目中的一个Maven项目(模块)

  • version:该元素定义的Maven项目所处的版本

  • packaging:该元素定义Maven项目的打包方式,当不定义packaging时Maven会使用默认值jar。

  • classfier:该元素用于定义构件输出的一些附属构件。附属构件与主构件对应,例如主构件是nexus-indexer-2.0.0.jar的项目还可以通过使用一些插件来生成例如nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar这样一些附属构件。

在上面五个元素中,groupId、artifactId、version是必须定义的,而packaging是可选的(默认为jar),而classfier是不能直接定义的。

依赖的配置

依赖管理时Maven的两大核心功能之一,它定义了一套成熟的依赖管理模式:

<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>....</exclusion>
...
</exclusions>
</dependency>
...
</dependencies>
  • groupId、artifactId、version:依赖的基本坐标,对任何一个依赖来说,基本坐标是最重要的。
  • type:依赖的类型,对应项目坐标定义中的packaging属性。
  • scope:依赖的范围。
  • optional:标记依赖是否可选
  • exclusions:用于排除传递性依赖。

依赖范围

​ Maven在编译主代码的时候会使用一套classpath;其次Maven在编译和执行测试的时候会使用另外一套classpath;最后,实际运行Maven项目的时候,又会使用一套classpath。而依赖范围就是用来控制依赖于这三种classpath(编译classpath、测试classpath、运行classpath)。

Maven有以下几种依赖范围

  • compile:编译依赖范围,如果未指定scope属性,那么该依赖默认范围是compile。compile也是我们项目中运用最多的依赖范围。

  • test:测试依赖范围,只在测试classpath中有效,在编译主代码时不能使用该此依赖。

  • provided:已提供依赖范围,例如servlet-api,我们将项目部署到服务器时,服务器会自动的提供这些依赖环境,但是我们在编译时又必须使用该依赖,否则编译不了,所以就有了provided依赖范围。

  • runtime:运行时依赖范围,有些依赖我们只需要在运行时加进去就可以了,例如MySQL驱动,由于MySQL驱动完全是按照java提供的规范编写,所有在Java自带的API中有对应的接口,我们面向接口编程不需要显示调用驱动中的代码,所以在代码编译时就不需要提供。

  • system:系统依赖范围。该依赖与三种classpath的关系和provided依赖范围完全一致。但是使用system范围的依赖时必须通过systemPath元素显示的指定依赖文件的路径。由于此依赖不是通过Maven仓库解析的,,而且往往与本机系统弄个绑定,可能造成构件的不可移植,因此需谨慎使用。

    <dependency>
    <groupId>javax.sql</groupId>
    <artifactId>jdbc-stdext</artifactId>
    <version>2.0</version>
    <scope>system</scope>
    <systemPath>${java.home}/lib/rt.jar</systemPath>
    </dependency>

依赖的传递

何为依赖传递

Maven的依赖传递大大减轻了我们对项目依赖文件的管理难度,如果不使用Maven如果我们要在自己的项目中搭建Spring环境,那么我们就需要去下载Spring的jar包,引入到本地项目中,但是Spring仍然需要依赖其它第三方的jar包,此时我们就必须查阅相关文档手动引入Spring所依赖的环境,这个非常繁琐。

Maven的依赖传递机制就很好的解决了这一问题,如果你在pom文件中引入spring-core那么Maven就会将spring-core和spring-core所依赖的环境都引入到工程中。

依赖范围影响依赖的传递

​ 依赖范围不仅可以控制依赖于三种classpath的关系,还对传递性依赖产生影响。假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是间接依赖。

​ 下图中,最左边一列代表第一直接依赖,最上面一行代表第二直接依赖:

​ 见上图我们可发现这样一个规律:当第二直接依赖为compile时,传递依赖与第一直接依赖一致;当第二直接依赖为test时,依赖不会得到传递;当第二直接依赖是provided时,只传递第一直接依赖为provided的依赖,且传递依赖依然为provided;当第二直接依赖是runtime时,传递依赖除compile以外与第一传递依赖一致。

表格的具体理解

​ 对于这张图,A->B和A->D就是第一直接依赖,B->C和D->E是第二直接依赖。第一和第二的概念是相对于现在所处的项目来说的。假如A项目使我们此时正在进行的项目,B、D都是本项目直接依赖的jar,那么C、E分别是B、D的直接依赖,那他们是否会随着B和D的引入而引入A项目呢?

​ 根据表格得知,compile-test是无效的,也就是C根本不会被引入到A项目中;而compile-runtime是runtime也就是说E会被引入A项目,并且在A项目中E的scope是runtime。

依赖仲裁三原则

  1. 版本声明原则

​ 优先按照依赖管理<dependencyManagement>元素中指定的版本声明进行仲裁,此时下面的两个原则都无效了。

  1. 最短路径原则

​ 在此种依赖关系下,gupao-web到底依赖的是1.1版本还是1.0版本的gupao-common-lib

​ 根据最短路径原则,在gupao-web依赖的是1.0版本的,Maven会自动过滤掉1.1版本的gupao-common-lib

  1. 最先声明原则

最短路径原则无法做出仲裁,那么就需要使用最先声明原则了,在这个案例中,gupao-common-lib到底依赖哪个版本取决于,gupao-bizgupao-dal谁先声明。

依赖冲突

何为依赖冲突

大多数的依赖冲突发生的原因是因为maven的传递依赖会引入很多隐式的依赖,这些依赖可能会和我们显示依赖版本不一致。

如图,我们显示依赖了 spring-boot1.5.9,和spring-core4.0.8(当然这种情况在正常情况下不会发生)在这种情况,根据Maven的最短依赖路径原则,会使用spring-core4.0.8,当在启动项目的时候会报错。这是因为spring-boot1.5.9运行所需要的spring-core版本是4.3.13,但是项目中编译的spring-core版本是4.0.8。

Maven冲突的实质是:不同版本的jar中会有部分API不一样,例如A依赖B的1.4版本中的某些新特性,但是系统根据仲裁法则选择了1.3版本的B,此时A所依赖的新特性用不了,这就导致项目无法运行。

如何解决依赖冲突

​ 冲突导致项目无法运行的原因是因为系统按照“三大依赖仲裁法则”留下的jar包的版本不适用于所有需求者,那么我们需要通过仲裁法则或者标签来解决冲突。 解决冲突的最终目的就是将我们需要的版本留下来,让系统忽略不兼容的版本。

​ 解决依赖有两大种方法:

  1. 利用依赖仲裁三原则选出我们需要的版本
  • 版本声明原则:我们在<dependencyManagement>锁定版本号。
  • 最短路径原则:我们直接在项目中显示引入需要的jar包版本即可。
  • 优先声明原则:我们将需要的版本放在前面声明
  1. 利用<exclution>标签添加排除

在IDEA中就会显示servlet-api冲突,(omitted for duplicate:省略重复的)

在当前这个情况中,我们以添加进行排除

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>

Maven到底是什么的更多相关文章

  1. maven(一) maven到底是个啥玩意~

    我记得在搞懂maven之前看了几次重复的maven的教学视频.不知道是自己悟性太低还是怎么滴,就是搞不清楚,现在弄清楚了,基本上入门了.写该篇博文,就是为了帮助那些和我一样对于maven迷迷糊糊的人. ...

  2. Maven到底是个啥玩意

    Maven,是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具. 上面是百度百科对Maven的正式介绍,如果你是Maven初学者,我估计你看完之后心中肯 ...

  3. 1.2.1 Maven到底是什么鬼

    解释之前,提1个小问题. 1.1.假如你正在Eclipse下开发两个Java项目,姑且把它们称为A.B,其中A项目中的一些功能依赖于B项目中的某些类,那么如何维系这种依赖关系的呢? 很简单,这不就是跟 ...

  4. maven(一) maven到底是什么

    为了方便自己查找,这里转载他人文章,原文出处http://www.cnblogs.com/whgk/p/7112560.html 我记得在搞懂maven之前看了几次重复的maven的教学视频.不知道是 ...

  5. 转帖 maven(一) maven到底是个啥玩意~

    转载自:https://www.cnblogs.com/whgk/p/7112560.html 我记得在搞懂maven之前看了几次重复的maven的教学视频.不知道是自己悟性太低还是怎么滴,就是搞不清 ...

  6. (十三)Maven插件解析运行机制

    这里给大家详细说一下Maven的运行机制,让大家不仅知其然,更知其所以然. 1.插件保存在哪里? 与我们所依赖的构件一样,插件也是基于坐标保存在我们的Maven仓库当中的.在用到插件的时候会先从本地仓 ...

  7. maven 详解

    Maven是基于项目对象模型(POM)的,可以通过一小段描述信息来管理项目构建,报告和文档的软件项目管理工具,是一种全新的项目构建方式,让我们的开发更加简单,高效.Maven主要做的是两件事: 开发规 ...

  8. maven 本地仓库和远程仓库

    在Maven中,任何一个依赖.插件或者项目构建的输出,都可以称之为构件. Maven在某个统一的位置存储所有项目的共享的构件,这个统一的位置,我们就称之为仓库.(仓库就是存放依赖和插件的地方) 任何的 ...

  9. Maven构件解析(转)

    文章转自http://gavinwind2000.iteye.com/blog/2290652 谢谢博主的总结! 在Maven中,任何一个依赖.插件或者项目构建的输出,都可以称之为构件. Maven在 ...

  10. [Maven]Maven 那点事儿

    0. 前言 Jason Van Zyl,在 Java 十大风云人物排行榜上或许会看到他. 这兄弟是干嘛的? 他就是 Maven 的创始人,人们都尊称他为"Maven 他爸". 毋庸 ...

随机推荐

  1. HarmonyOS:NativeWindow 开发指导

      场景介绍 NativeWindow是HarmonyOS本地平台化窗口,表示图形队列的生产者端.开发者可以通过NativeWindow接口进行申请和提交Buffer,配置Buffer属性信息. 针对 ...

  2. HarmonyOS Connect FAQ第三期

    原文:https://mp.weixin.qq.com/s/YpI9-k4yQvNhaMfg7Li82g,点击链接查看更多技术内容.   在开发HarmonyOS Connect生态产品时,你是否对设 ...

  3. 全面支持JS/eTS应用开发,DevEco Studio 3.0 Beta4新版本发布

    原文:https://mp.weixin.qq.com/s/j5Cl48ZxzEmnnpfoM0pKJg ,点击链接查看更多技术内容. HUAWEI DevEco Studio(后文简称DevEco ...

  4. nginx重新整理——————http 模块中的请求过程[十一]

    前言 简单介绍一下http的一些指令. 正文 一般http的嵌套规则是这样的: http{ upstream{} split_clients {} map{} gep{} server{ if(){} ...

  5. mongodb基础整理篇————聚合操作[三]

    前言 简单整理一下聚合操作. 正文 什么是聚合框架: 作用于一个或多个集合上 对集合的数据进行的一系列运算 将这些数据转换为期望的形式 从效果而言, 聚合框架相当于SQL 查询中的: Group By ...

  6. 重新点亮linux 命令树————压缩和解压缩[四]

    前言 简单整理一下压缩和解压缩. 正文 在windows 中我们使用压缩和解压缩一般是7z这个压缩和解压软件,但是在linux中压缩和解压是两个不同的软件. 在最早的linux 备份介质是磁带,使用的 ...

  7. Java面试题:请谈谈对ThreadLocal的理解?

    ThreadLocal是一种特殊的变量存储机制,它提供了一种方式,可以在每个线程中保存数据,而不会受到其他线程的影响.这种机制在多线程编程中非常有用,因为它允许每个线程拥有自己的数据副本,从而避免了数 ...

  8. 当 Knative 遇见 WebAssembly

    简介: Knative 可以支持各种容器化的运行时环境,我们今天来探索一下利用 WebAssembly 技术作为一个新的 Serverless 运行时. 作者:易立 Knative 是在 Kubern ...

  9. DataFunTalk:阿里建设一站式实时数仓的经验分享

    简介: 本文内容整理于阿里资深技术专家姜伟华在DataFunTalk上的演讲,为大家介绍阿里巴巴基于一站式实时数仓Hologres建设实时数仓的经验和解决方案. 导读:大数据计算正从规模化走向实时化, ...

  10. [MySQL] 导入数据库和表的两种方式

    如果是导入 mysqldump 导出的 sql 文件,使用 mysql 命令再导入就可以了. 下面是另一种可选方式: use xxdb source /var/lib/mysql/xxtable.sq ...