maven 介绍

来源:互联网 发布:linux 终端切换 编辑:程序博客网 时间:2024/06/07 05:34

这篇文章是入职之初为了解maven,查资料写在未知笔记中,整理下转到这里来

1,Maven简介

1.1 maven 是什么

maven 是一个项目构建和管理工具,通过maven可以实现:
版本 – maven有自己的版本定义和规则
依赖关系 – 对依赖关系的特性进行分析和划分,避免开发过程中的依赖混乱和相互污染行为
项目关系 – 一个大型的项目通常有几个小项目或者模块组成,用maven可以很方便地管理
移植性管理 – maven可以针对不同的开发场景,输出不同种类的输出结果
Maven 使用约定优于配置的原则,maven默认的文件存放结构如下:
这里写图片描述

优点:

  • 减少修改配置的工作量
  • 学习成本
  • 给项目引入了统一的规范

1.2 maven 安装

  • 下载安装包并解压
  • 在~/bash_profile 中配置环境变量,通过mvn -v 检验是否安装成功
  • 设置settings.xml 文件
    settings.xml :定义Maven的全局环境信息,定义本地仓库、远程仓库和联网使用的代理信息等。
    Maven安装目录的conf子目录下面的settings.xml是真正的全局的配置;users/.m2目录下的settings.xml的配置是针对当前用户的,当这两个文件同时存在的时候,后者会覆盖前者。
    settings.xml中主要包括以下元素:
    • localRepository:储存信息的本地仓库的目录。默认是.m2/repository
    • servers:定义一系列的server子元素,表示当需要连接到一个远程服务器的时候需要使用到的验证方式
    • mirrors:用于定义一系列的远程仓库的镜像
    • profiles:用于指定一系列的profile
      profile是用来定义一些在构建中使用的environmental variations,profile可以设置成在不同的环境下激活不同的profile(例如:不同的OS激活不同的profile,不同的JVM激活不同的profile,不同的dabase激活不同的profile等等

1.3 本地仓库与远程仓库

采用远程仓库和本地仓库以及pom.xml,将pom.xml中定义的jar文件从远程仓库下载到本地仓库,各个应用使用同一个本地仓库的jar,同一个版本的jar只需下载一次。
这里写图片描述

maven的仓库只有两大类:本地仓库;远程仓库。在远程仓库中又分成了3种:中央仓库 ;私服 ;镜像仓库。

  • 本地仓库:默认是.m2/repository
  • 中央仓库包:http://search.maven.org/,含了绝大多数流行的开源Java构件,以及源码、作者信息、SCM、信息、许可证信息等
  • 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,当Maven需要下载构件的时候,从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,缓存在私服上之后,再为Maven的下载请求提供服务
    这里写图片描述

maven 在仓库解析依赖的机制:

1,当依赖的范围是system的时候,Maven直接从本地文件系统解析构件
2,根据依赖坐标计算仓库路径后,首先从本地仓库寻找构件,如果发现相应构件,则解析成功
3,在本地仓库不存在相应的构件,如果依赖的版本是显示的发布版本构件,则遍历所有的远程仓库,发现后下载使用
4,如果依赖的版本是SNAPSHOT, 则基于更新策略读取远程仓库的版本, 将其与本地仓库的版本对比,得到最新快照版本的值,然后基于该值检查本地仓库或从远程仓库下载

2,依赖管理

Maven的核心特征就是依赖管理, Maven通过读取依赖项项目文件(pom.xml中),找出项目的依赖等。
Maven 通过pom(Project Object Model,项目对象模型)定义了项目的基本信息,描述项目是如何构建,声明项目依赖,插件配置,仓库配置等等。
这里写图片描述

2.1 坐标标签

这里写图片描述
首先是project 元素 ,它是所有pom.xml的根元素,其中的groupId,artifactId以及version 定义了一个项目的坐标,在maven的世界,任何的jar,pom或者war 都是以基于这些基本的坐标进行区分的。

  • groupId : 定义当前maven项目隶属的实际项目
  • artifactId : 定义实际项目中的一个Maven项目,推荐使用实际项目作为artifactId的前缀
  • version : maven项目当前所处的版本
  • packaging : 打包的格式,默认为jar

2.2 仓库依赖

dependency list是POM的重要部分,也就是我们项目对jar包的管理。

  • groupId , artifactId , version :引用的坐标
  • scope : compile(default),provided,runtime,test,system 依赖的范围
  • exclusions 需要排除的依赖的jar包

依赖范围:
1. compile 默认的范围。编译依赖范围。使用此范围,对编译、测试、运行三种都有效。如spring-core。
2. test 测试依赖范围。使用此范围,只对测试classpath有效。如junit。
3. provided 已提供依赖范围。使用此范围,对编译和测试的classpath有效,运行无效。
4. runtime 运行时依赖范围。使用此范围,对测试和运行有效,但编译时无效。如JDBC驱动实现。编译时只需要jdk提供的jdbc接口即可,测试和运行时才需要实现的驱动。
5. system 系统依赖范围。使用该范围,必须通过systemPath元素显示指定依赖文件路径,不是通过maven仓库解析,可能造成构建不可移植,慎用。
6. import 导入依赖范围。该范围不会对classpth产生实际影响。一般都指向打包为pom的模块。在继承和聚合部分使用

Maven 中引入传递依赖机制
如果 A 依赖了 B,而 B 又依赖了 C ,那么 B 对 C 的依赖是会传递给 A 的。因此只需要关心项目的直接依赖是什么,不用考虑这些直接依赖会引入什么传递性依赖。
缺点:同一个包的多个依赖 ,Maven 依赖解调原则:

  1. 路径最近者优先

    A -> B -> C -> X(1.0)
    A -> D -> X(2.0)
    取:X(2.0)

  2. 路径长度一样时,声明顺序靠前的会被采用

传递性会给项目隐式的引入很多依赖,但是有时也会带来版本冲突,这时就需要手工选择需要的版本,使用exclusion可以排除掉依赖jar包间接依赖的包

2.3 继承和聚合

  1. 聚合:想要一次构建多个项目模块,就需要对多个项目模块进行聚合
    需要在聚合模块pom中设置
    最后在聚合模块(demo-parent)的pom上面 运行mvn命令 构建多个模块。demo-service-base 被声明为一个 pom ,他不包含任何的具体实现,一般将子模块作为这个项目的子目录。构建时 maven 会先解析聚合模块的 pom ,然后分析要构建的模块以及各个模块间的依赖顺序,按照计算出的顺序去构建各个模块。
  2. 继承:为了消除重复,把很多相同的配置提取出来。可以在父模块中声明一些公共的依赖,这样在子模块中便不再需要重复性定义。

但如果新加入一个子模块,如何保证父模块中的依赖项对子模块也是需要的呢?
解决办法 就是在父POM 中使用dependencyManagement 声明依赖,
在 dependencyManagement 下声明的依赖并不会被子模块实际引入,但可以通过在其中定义好version 来起到约束作用,只有在子模块加入元素声明父模块,时才会继承 dependencyManagement 中该依赖项的相关配置。
使用时可以在子模块中只指定 groupId 和 artifactId ,而继承使用 parent 的 dependencyManagement 中定义的 version、scope,此种配置方式的好处在于有一个地方可以集中管理所有依赖项的 version 和 scope,同时又不失子模块配置依赖的灵活性。

3,版本

maven 的版本管理是针对构件而言的(就是jar包),约定 < 主版本 >.< 次版本 >.< 增量版本 >-< 限定版本号 > ,比如2.2.0-SNAPSHOT,1.2.0

  • 主版本 : 表示了项目的重大架构变更
  • 次版本 : 表示较大范围的功能增加和变化
  • 增量版本 : 一般表示重大Bug修复
    对于主版本、次版本、增量版本来说他们的比较是基于数字的,对于限定版本来说,比较是基于字符串的,以版本号“1.2.3-alpha-2”和“1.2.3-alpha-10”为例,“alpha-2”和“alpha-10”是使用字符串进行比较的。

【SNAPSHOT】

Maven版本可以包含一个字符串字面量来表示项目正处于活动的开发状态。如果一个版本包含字符串“SNAPSHOT”,Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值。例如,项目有个版本为“1.0-SNAPSHOT”并且这个项目的构件部署到了一个Maven仓库,如果你在UTC2008年2月7号下午11:08部署了这个版本,Maven就会将这个版本展开成“1.0-20080207-230803-1”。
SNAPSHOT 版本在项目活动的开发过程中使用。如果项目依赖的一个组件正处于开发过程中,可以依赖于一个SNAPSHOT版本,在运行构建的时候Maven会自动从私服中检查它所依赖的构件 SNAPSHOT 的最新版本,当发现有更新的时候便自动进行下载。

4, maven的生命周期

maven把项目的构建划分为不同的生命周期(lifecycle),maven有三套相互独立的生命周期,分别是:
1. Clean Lifecycle 在进行真正的构建之前进行一些清理工作。
2. Default Lifecycle
3. Site Lifecycle 生成项目报告,站点,发布站点。
其中 default 是构建的核心部分,主要包括:
这里写图片描述
每个生命周期包含一些阶段(phase),这些阶段(phase)是有顺序的,每个阶段蕴含一个或多个目标(goal),并且后面的阶段依赖于前面的阶段,我们和Maven最直接的交互方式就是调用这些生命周期阶段。 较之于有先后顺序依赖的生命周期内的各阶段,三套生命周期之间本身是相互独立的。

maven 插件

maven为程序的构建定义了一套规范流 程:第一步需要validate,第二步需要initialize… … compile,test,package,… … install,deploy,但是并没有定义每一个phase具体应该如何操作。这里的phase的作用有点类似于Java语言中的接口,只协商了一个 契约,但并没有定义具体的动作。这里具体的动作就是由goal来定义,一个goal在maven中就是一个Mojo(Maven old java object)。Mojo抽象类中定义了一个execute()方法,一个goal的具体动作就是在execute()方法中实现。实现的Mojo类放在maven plugin里,就是实际的构建任务会交给差价去做,所谓的plugin其实也就是一个maven项目,只不过这个项目会引用maven的一些API,plugin项目也具备maven坐 标。
在执行具体的构建时,我们需要为lifecycle的每个phase都绑定一个goal,这样才能够在每个步骤执行一些具体的动作。比如 在lifecycle中有个编译compile 阶段,,而maven-compile-plugin这个插件有个compile goal就是用javac来将源文件编译为class文件的,我们需要做的就是在pom的 中将compile这个phase和maven-compile- plugin中的compile这个goal进行绑定,这样就可以实现lifecycle 中的编译,也就是Java源代码的编译了。
为了能让我们不用任何配置就能构建maven项目,maven已经为一些主要的生命周期绑定了插件目标。当用户通过命令行调用生命周期阶段的时候,对应的插件目标会执行相应任务。
这里写图片描述

当首次执行compile命令或其它命令时,maven才会下载所有插件和相关的文件,而之后再执行同一个命令的时候会发现比第一次快很多,这就为什么首次执行命令时候会比较慢的原因。
maven 声明周期与插件的绑定图:
这里写图片描述

Maven是如何知道从哪里找到要编译的源文件?并且Maven如何知道将编译好的类文件放到哪里?这里就是由Mave基础工作之一“通过配置进行约定”所解决的问题。一般情况下,源文件放在src/main/java路径下,这种默认设置是从Super POM继承来的。即使最简单的POM也知道源文件的默认位置。

参考:https://www.douban.com/note/511279110/
http://yinny.iteye.com/blog/1883488;
http://blog.csdn.net/totogogo/article/details/1956381