你需要知道的 maven 基础知识

这里记录 maven 的基础知识,一方面巩固自己学到的知识,另一方面希望可以对有同样困惑的小伙伴提供一些帮助。

一. maven 简介

Maven 是一个项目管理工具,它包含了一个项目对象模型POM (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System)和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。 当你使用Maven的时候,你用一个明确定义的 POM 来描述你的项目,然后 Maven 可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。

Maven 的核心其实不做什么实际的事情,除了解析一些 XML 文档,管理生命周期与插件之外,它什么也不懂。Maven 被设计成将主要的职责委派给一组 Maven 插件,这些插件可以影响 Maven 生命周期,提供对目标的访问。绝大多数 Maven 的动作发生于Maven 插件的目标(goal),如编译源码,打包二进制代码,发布站点和其它构建任务。你从 Apache 下载的 Maven 不知道如何打包 WAR 文件,也不知道如何运行单元测试,Maven 大部分的智能是由插件实现的,而插件从 Maven 仓库获得。事实上,第一次你用全新的 Maven 安装运行诸如 mvn install 命令的时候,它会从中央 Maven 仓库下载大部分核心 Maven 插件。这不仅仅是一个最小化 Maven 分发包大小的技巧,这种方式更能让你升级插件以给你项目的构建提高能力。Maven 从远程仓库获取依赖和插件的这一事实允许了构建逻辑的全局性重用。

使用下面的命令可以查看指定插件的详细信息:

mvn help:describe -Dplugin=install -Dfull

二. maven 项目依赖

Maven可以管理内部和外部依赖。内部依赖是指一个 maven 项目内部各个模块之间的依赖,比如一个三层架构的 web 项目中 service 模块依赖 model 模块,或者持久层模块;外部依赖是指依赖第三方类库,比如依赖 spring、mybatis 等相关的依赖。

1. 依赖范围

pom.xml 文件中指定 dependency 依赖时,可以指定依赖的 scope 范围,范围可以控制哪些依赖在哪些classpath 中可用,哪些依赖包含在一个应用中。让我们详细看一下每一种范围:

  • compile(编译范围)

    compile 是默认的范围。如果没有提供一个范围,那该依赖的范围就是编译范围。编译范围依赖在所有的 classpath 中可用,同时它们也会被打包。

  • provided(已提供范围)

    provided 依赖只有在当 JDK 或者一个容器已提供该依赖之后才使用。例如,如果你开发了一个web应用,你可能在编译 classpath 中需要可用的 Servlet API 来编译一个 servlet,但是你不会想要在打包好的WAR中包含这个Servlet API,这个Servlet API JAR由你的应用服务器或者servlet 容器提供。已提供范围的依赖在编译classpath(不是运行时)可用,它们不是传递性的,也不会被打包。

  • runtime(运行时范围)

    runtime 依赖在运行的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要 JDBC API JAR 接口,而只有在运行的时候才需要 JDBC 驱动实现。

  • test(测试范围)

    test 范围依赖在一般的编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用

  • system(系统范围)

    system 范围依赖与 provided 类似,但是你必须显式的提供一个对于本地系统中JAR文件的路径。这么做是为了允许基于本地对象编译,而这些对象是系统类库的一部分。这样的构件应该是一直可用的,Maven也不会在仓库中去寻找它。如果你将一个依赖范围设置成系统范围,你必须同时提供一个systemPath元素。注意该范围是不推荐使用的(你应该一直尽量去从公共或定制的Maven仓库中引用依赖)。

    scope 取值 有效范围(compile, runtime, test) 依赖传递 例子
    compile(默认) all spring-core
    provided compile、test servlet-api
    runtime runtime、test JDBC驱动
    test test Junit
    system compile、test 不推荐使用

2. 可选依赖

在 pom.xml 文件中指定<dependency>依赖时,可以通过 <optional>true<optional>标签将此依赖声明为可选依赖。如果项目中依赖的类库包含可选依赖,我们在使用的时候需要显示指定需要的可选依赖。

3. 传递依赖

传递性依赖就是对于一个依赖的依赖。如果 project-a 依赖于 project-b,而后者接着依赖于 project-c,那么 project-c 就被认为是 project-a 的传递性依赖。如果 project-c 依赖于 project-d,那么project-d 就也被认为是 project-a 的传递性依赖。Maven 的部分优势是由于它能够管理传递性依赖,并且能够帮助开发者屏蔽掉跟踪所有编译期和运行期依赖的细节。你可以只依赖于一些包如 Spring Framework,而不用担心 Spring Framework 的所有依赖,Maven 帮你自动管理了,你不用自己去详细了解配置。

Maven 是怎样完成这件事情的呢?它建立一个依赖图,并且处理一些可能发生的冲突和重叠。如果Maven 看到有两个项目依赖于同样的 groupId 和 artifactId,它会自动选择那个最新版本的依赖。虽然这听起来很方便,但在一些边界情况中,传递性依赖会造成一些配置问题。在这种情况下,你可以使用<exclusion>依赖排除。

下面几种情况可以使用<exclusion>依赖排除:

  1. 排除通过传递依赖引入但是项目中没有使用的依赖
  2. 排除运行时容器已经提供的依赖
  3. 排除可能有多个实现的 API 依赖
  4. 有多个版本的依赖,排除你不想使用的那个依赖

4. 依赖管理

在一个有很多模块的复杂 maven 项目中,如果有很多模块都使用了相同的依赖,比如 MySQL 数据库驱动的依赖,需要在各个模块中独立的列出该依赖的版本,在你需要升级到一个新版本的时候你就会遇到问题。由于这些版本号分散在你的项目树中,你需要手动修改每一个引用该依赖的 pom.xml,确保每个地方的版本号都更改了。那么如何解决这个问题呢?

maven 提供了 dependencyManagement元素来统一管理依赖版本号,使用 pom.xml中的dependencyManagement 元素能让你在子项目中引用一个依赖而不用显式的列出版本号,maven 会沿着父子层次向上走,直到找到一个拥有 dependencyManagement 元素的项目,然后它就会使用在这个

dependencyManagement 元素中指定的版本号。

注意 如果子项目定义了一个版本 version,它将覆盖顶层 POM 的 dependencyManagement 元素中的版本。只有在子项目没有直接声明一个版本的时候,dependencyManagement 定义的版本才会被使用。

定义在顶层 pom.xml 文件的 dependencyManagement 中的依赖,在子项目中如果没有显示引用则不会被引入,但是定义顶层 pom.xmldependencies 中的依赖会被所有子项目引入,为了不添加一些不必要的依赖,使用 dependencyManagement 能让你统一并集中化依赖版本的管理,而不用添加那些会被所有子项目继承的依赖。换句话说,dependencyManagement元素和一个环境变量一样,能让你在一个项目下面的任何地方声明一个依赖而不用指定一个版本号。

三. maven 生命周期

1. maven默认生命周期

生命周期阶段 描述
validate 验证项目是否正确,以及所有为了完整构建必要的信息是否可用
generate-sources 生成所有需要包含在编译过程中的源代码
process-sources 处理源代码,比如过滤一些值
generate-resources 生成所有需要包含在打包中的资源文件
process-resources 复制并处理资源文件至目标(target)目录,准备打包
compile 编译项目的源代码
process-classes 后处理编译生成的 class 文件,例如对Java类进行字节码增强
generate-test-sources 生成所有包含在测试编译过程中的测试源码
process-test-sources 处理测试源码,比如过滤一些值
generate-test-resources 生成测试需要的资源文件
process-test-resources 复制并处理测试资源文件至测试目标目录
test-compile 编译测试源码至测试目标目录
test 使用合适的单元测试框架运行测试,这些测试不需要被打包或发布
prepare-package 在真正的打包之前,执行一些准备打包必要的操作
package 将编译好的代码打包成可分发的格式,如 JAR,WAR
pre-integration-test 执行一些在集成测试运行之前需要的动作。如建立集成测试需要的环境
integration-test 如果有必要的话,处理包并发布至集成测试可以运行的环境
post-integration-test 执行一些在集成测试运行之后需要的动作。如清理集成测试环境。
verify 执行所有检查,验证包是有效的,符合质量规范
install 安装包至本地仓库,以备本地的其它项目作为依赖使用
deploy 安装包至远程仓库,共享给其他开发人员和项目

2. 执行 mvn package 发生了什么

image-20210714192223631.png

我们可以将插件的目标绑定到 maven 的生命周期阶段上,执行 mvn package 命令,在 Maven 沿着生命周期一步步向前的过程中,它将运行绑定在每个阶段上的所有目标。

四. maven 内置属性

1. maven 属性

你可以在 pom.xml 文件或者资源文件中使用属性,资源文件被会 Maven Resource 插件的过滤特性处理。一个属性永远包含在中${}中。例如,要引用 project.version 属性,就需要这样写 ${project.version}。在任何Maven项目中都有一些隐式的属性,这些隐式的属性是:

  • project.*

    可以使用该 project.* 前缀来引用任何在 pom.xml 中的值。

  • settings.*

    可以使用该 settings.* 前缀来引用 ~/.m2/settings.xml 文件中的值。

  • env.*

    可以使用 env.* 前缀来引用环境变量如 PATHM2_HOME的值。

  • 系统属性

    任何可以通过 System.getProperty() 方法获取的属性都可以作为 maven 属性被引用。

可以使用 mvn help:system 命令可查看所有环境变量。

除了上述的隐式属性,用户还可以使用 <properties> 自定义属性。

2. pom.xml 文件常用属性

${project.groupId}:项目的groupId.
${project.artifactId}:项目的artifactId.
${project.version}:项目的version,于${version}等价 
${project.basedir}:表示项目根目录,即包含pom.xml文件的目录;  
${project.build.sourceDirectory}:项目的主源码目录,默认为src/main/java/.
${project.build.testSourceDirectory}:项目的测试源码目录,默认为/src/test/java/.
${project.build.directory}:项目构建输出目录,默认为target/.
${project.outputDirectory}:项目主代码编译输出目录,默认为target/classes/.
${project.testOutputDirectory}:项目测试代码编译输出目录,默认为target/testclasses/.
${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}

3. setting.xml 文件属性

与pom属性同理,用户可以用以settings.开头的属性引用 setting.xml 文件的XML元素值。

${settings.localRepository} 表示本地仓库的地址

4. 自定义属性

自定义属性:在 pom.xml 文件的 <properties> 标签下定义的maven属性:

<properties>
    <spring>5.2.13.RELEASE</spring>
</properties>

可以在其他地方使用自定义的属性:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring}</version>
</dependency>

五. POM 最佳实践

1. 依赖归类

如果你有一组逻辑上归类在一起的依赖,你可以创建一个打包方式为 pom 的子模块来将这些依赖归在一起。比如创建一个叫做 dependencies 的子模块,此模块只包含一个 pom.xml 文件,用来归类管理一些公共依赖的版本号,注意指定此模块的打包类型为:<packaging>pom</packaging>

比如在 dubbo 项目中也是这样使用的,它定义了一个叫做 dubbo-dependencies-bom 的子模块,模块中只有一个 pom.xml 文件,如下图:

image-20210716102222998.png

dubbo-dependencies-bom 的父 pom.xml 中通过如下方式引入使用:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-bom</artifactId>
                <version>${project.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

参考文档

maven 指南

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 简介 概述 Maven 是一个项目管理和整合工具 Maven 为开发者提供了一套完整的构建生命周期框架 Maven...
    闽越布衣阅读 4,207评论 6 39
  • 坐标 maven管理项目依赖的底层基础-坐标。坐标元素:groupId、artifactId、version、pa...
    破晓追风阅读 395评论 0 0
  • 简介 概述 Maven 是一个项目管理和整合工具 Maven 为开发者提供了一套完整的构建生命周期框架 Maven...
    caoxinyiyi阅读 182评论 0 0
  • 1. maven介绍: maven 是一个软件构建和管理工具,Maven可以利用中心信息片断管理项目的构建、报告和...
    墨笛春秋阅读 230评论 0 0
  • 五. 仓库 在Maven中,任何一个依赖、插件或者项目构建的输出,都可以称之为 构件。Maven在某个统一的位置存...
    wind_sky阅读 1,348评论 0 0