Maven使用指南

Maven是什么

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

Maven项目模型

Maven 维护了一个项目的模型,你不仅仅需要把源码编译成字节码,你还需要开发软 件项目的描述信息,为项目指定一组唯一的坐标。你要描述项目的的属性。项目的许可证是什么?谁开发这个项目,为这个项目做贡献?这个项目依赖于其它什么项目没有?

Maven不仅仅是一个“构建工具”,它不仅仅是在类似于 make 和 Ant 的工具的基础上 的改进,它是包含了一组关于软件项目和软件开发的语义规则的平台。这个基于每一个项目定义的模型实现了如下特征:

依赖管理

由于项目是根据一个包含组标识符,构件标识符和版本的唯一的坐标定义的。项目间可以使用这些坐标来声明依赖。

远程仓库

和项目依赖相关的,我们可以使用定义在项目对象模型(POM)中的坐标来创建 Maven 构件的仓库。

全局性构建逻辑重用

插件被编写成和项目模型对象(POM)一起工作,它们没有被设计成操作某一个 已知位置的特定文件。一切都被抽象到模型中,插件配置和自定义行为都在模型 中进行。

工具可移植性/集成

像 Eclipse,NetBeans,和 InteliJ 这样的工具现在有共同的地方来找到项目 的信息。在 Maven 出现之前,每个 IDE 都有不同的方法来存储实际上是自定义 项目对象模型(POM)的信息。Maven 标准化了这种描述,而虽然每个 IDE 仍然 继续维护它的自定义项目文件,但这些文件现在可以很容易的由模型生成。

便于搜索和过滤构件

像 Nexus 这样的工具允许你使用存储在 POM 中的信息对仓库中的内容进行索引 和搜索。

Maven 为软件项目的语义一致性描述的开端提供了一个基础。

安装Maven

通过sdkman安装

安装sdkman

sdkman.io/install

$ curl -s "https://get.sdkman.io" | bash
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
$ sdk version
复制代码

安装maven最新版本

$ sdk install maven
$ mvn -v
复制代码

用户相关配置

~/.m2/settings.xml
复制代码

该文件包含了用户相关的认证,仓库和其它信息的配置,用来自定义Maven的行 为。

~/.m2/repository/
复制代码

该目录是你本地的仓库。当你从远程Maven仓库下载依赖的时候,Maven在你本地 仓库存储了这个依赖的一个副本。

POM模型

Maven项目,依赖,构建配置,以及构件:所有这些都是要建模和表述的对象。这些对 象通过一个名为项目对象模型(Project Object Model, POM)的XML文件描述。这个POM 告诉Maven它正处理什么类型的项目,如何修改默认的行为来从源码生成输出。该文件是Maven中一个项目的描述性陈述;也 是当Maven构建项目的时候需要理解的一份“地图”。

POM坐标

坐标为一个项目定义一个唯一的位置,项目使用Maven坐标与其它项目关联。一个项目不是简单的依赖于另一个 项目,而是一个带有groupId,artifactId,和version的项目依赖于另一个带有 groupId,artifactId,和version的项目

groupId

一个groupId归类了一组相关的构件。组定义符基本上类似于一个Java包 名。例如:groupId org.apache.maven是所有由Apache Maven项目生成的构

件的基本groupId。组定义符在Maven仓库中被翻译成路径,例如,groupId org.apache.maven可以在repo1.maven.org1 的/maven2/org/apache/maven目录下找到。

artifactId

artifactId是项目的主要定义符。当你生成一个构件,这个构件将 由artifactId命名。当你引用一个项目,你就需要使用artifactId来引用它。artifactId和groupId的组合必须是唯一的。换句话说,你 不能有两个不同的项目拥有同样的artifactId和groupId;在某个特定的groupId下,artifactId也必须是唯一的。

version

当一个构件发布的时候,它是使用一个版本号发布的。该版本号是一个数字定 义符如“1.0”,“1.1.1”,或“1.1.2-alpha-01”。你也可以使用所谓的快照(snapshot)版本。一个快照版是一个处于开发过程中的组件的版本,快照版本 号通常以SNAPSHOT结尾;如,“1.0-SNAPSHOT”,“1.1.1-SNAPSHOT”,和“1SNAPSHOT”

classifier(限定符)

如果你要发布同样的代码,但是由于技术原因需要生成两个单独的构件,你就 要使用一个分类器(classifier)。例如,如果你想要构建两个单独的构件成 JAR,一个使用Java 1.4编译器,另一个使用Java 6编译器,你就可以使用分 类器来生成两个单独的JAR构件,它们有同样的groupId:artifactId:version组 合。如果你的项目使用本地扩展类库,你可以使用分类器为每一个目标平台生成 一个构件。分类器常用于打包构件的源码,JavaDoc或者二进制集合。

超级POM

超级POM定义了一些由所有项目继承的标准配置变量,类似于Java中的 Object类

  • 默认的超级POM定义了一个单独的远程Maven仓库,ID为central。这是所有 Maven客户端默认配置访问的中央Maven仓库。该配置可以通过一个自定义 的settings.xml文件来覆盖。注意这个默认的超级POM关闭了从中央Maven仓 库下载snapshot构件的功能。如果你需要使用一个snapshot仓库,你就要在 你的pom.xml或者settings.xml中自定义仓库设置。Settings和profile将会 在第 11 章构建Profile中和第 A.1 节 “简介”中的附录 A,附录: Settings 细节小节中具体介绍。
  • 中央Maven仓库同时也包含Maven插件。默认的插件仓库就是这个中央仓 库。Snapshot被关闭了,而且更新策略被设置成了“从不”,这意味着Maven将永 远不会自动更新一个插件,即使新版本的插件发布了。
  • build元素设置Maven标准目录布局中那些目录的默认值。
  • 从Maven 2.0.9开始,超级POM为核心插件提供了默认版本。这么做是为那些没有 在它们POM中指定插件版本的用户提供一些稳定性。

有效POM

查看有效POM

mvn help:effective-pom
复制代码

IDEA查看方法:选择pom文件->右键菜单->Maven->Show Effective POM

POM语义

Maven项目中的POM永远都是基础目录下的一个名为pom.xml的文件

项目版本

一个Maven项目发布版本号用version编码,用来分组和排序发布。Maven中的版本包含 了以下部分:主版本,次版本,增量版本,和限定版本号。一个版本中,这些部分对应 如下的格式:

<major version>.<minor version>.<incremental version>-<qualifier>
复制代码

例如:版本“1.3.5”由一个主版本1,一个次版本3,和一个增量版本5。而一个 版本“5”只有主版本5,没有次版本和增量版本。限定版本用来标识里程碑构建: alpha和beta发布,限定版本通过连字符与主版本,次版本或增量版本隔离。例如,版 本“1.3-beta-01”有一个主版本1,次版本3,和一个限定版本“beta-01”。

SNAPSHOT版本

Maven版本可以包含一个字符串字面量来表示项目正处于活动的开发状态。如果 一个版本包含字符串“SNAPSHOT”,Maven就会在安装或发布这个组件的时候将该 符号展开为一个日期和时间值,转换为UTC(协调世界时)。例如,如果你的项目 有个版本为“1.0-SNAPSHOT”并且你将这个项目的构件部署到了一个Maven仓库, 如果你在UTC2008年2月7号下午11:08部署了这个版本,Maven就会将这个版本展开 成“1.0-20080207-230803-1”。换句话说,当你发布一个snapshot,你没有发布一个 软件模块,你只是发布了一个特定时间的快照版本。

那么为什么要使用这种方式呢?SNAPSHOT版本在项目活动的开发过程中使用。如果你的 项目依赖的一个组件正处于开发过程中,你可以依赖于一个SNAPSHOT版本,在你运行 构建的时候Maven会定期的从仓库下载最新的snapshot。类似的,如果你系统的下一个 发布版本是“1.4”你的项目需要拥有一个“1.4-SNAPSHOT”的版本,之后它被正式发 布。

作为一个默认设置,Maven不会从远程仓库检查SNAPSHOT版本,要依赖于SNAPSHOT版 本,用户必须在POM中使用repository和pluginRepository元素显式的开启下载snapshot

的功能。

当发布一个项目的时候,你需要解析所有对SNAPSHOT版本的依赖至正式发布的版本。 如果一个项目依赖于SNAPSHOT,那么这个依赖很不稳定,它随时可能变化。发布到非 snapshot的Maven仓库(如repo1.maven.org/maven2)的构件不… SNAPSHOT版本,因为Maven的超级POM对于中央仓库关闭了snapshot。SNAPSHOT版本只用 于开发过程。

LATEST 和 RELEASE 版本

当你依赖于一个插件或一个依赖,你可以使用特殊的版本值LATEST或者 RELEASE。

LATEST是指某个特定构件最新的发布版或者快照版(snapshot),最近被部署 到某个特定仓库的构件。

RELEASE是指仓库中最后的一个非快照版本。总得来说,设计 软件去依赖于一个构件的不明确的版本,并不是一个好的实践。

如果你处于软件开发过 程中,你可能想要使用RELEASE或者LATEST,这么做十分方便,你也不用为每次一个第 三方类库新版本的发布而去更新你配置的版本号。

但当你发布软件的时候,你总是应该 确定你的项目依赖于某个特定的版本,以减少构建的不确定性,免得被其它不受你控制 的软件版本影响。如果无论如何你都要使用LATEST和RELEASE,那么要小心使用

项目依赖范围

compile(编译范围)

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

162 项目对象模型

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仓库中引用 依赖)。

依赖版本管理

Maven在dependencyManagement元素中为你提供了一种方式来统一依赖版 本号,在项目中确保对指定依赖版本号只描述一次,减少后续版本升级的工作量

依赖冲突管理

排除依赖

<dependency> 
    <groupId>org.sonatype.mavenbook</groupId> 
    <artifactId>project-a</artifactId> 
    <version>1.0</version> 
    <exclusions> 
        <exclusion> 
            <groupId>org.sonatype.mavenbook</groupId> 
            <artifactId>project-b</artifactId> 
        </exclusion> 
    </exclusions> 
 </dependency>
复制代码

更新依赖版本

<dependencies> 
    <dependency> 
        <groupId>org.hibernate</groupId> 
        <artifactId>hibernate</artifactId> 
        <version>3.2.5.ga</version> 
        <exclusions> 
            <exclusion>
                <groupId>javax.transaction</groupId> 
                <artifactId>jta</artifactId> 
            </exclusion> 
        </exclusions> 
    </dependency> 
    <dependency> 
        <groupId>org.apache.geronimo.specs</groupId> 
        <artifactId>geronimo-jta_1.1_spec</artifactId> 
        <version>1.1</version> 
    </dependency> 
</dependencies>
复制代码

Maven构建生命周期

一个Maven生命周期包含了一些有序的命名阶段:prepareresources,compile,package,和install以及其它。有个阶段抽象了编译过程,有个 阶段抽象了打包过程。而那些pre-和post-阶段可以用来注册一些必须在某些特定阶段 之前或之后运行的目标。当你让Maven构建一个项目的时候,你其实是让它一步步通过 那些预定义的有序的阶段,并且运行所有注册到某个特定阶段的目标。

一个构建生命周期是一组精心组织的有序的阶段,它的存在能使所有注册的目标变得 有序运行。这些目标根据项目的打包类型被选择并绑定。Maven中有三种标准的生命周 期:清理(clean),默认(default)(有时候也称为构建),和站点(site)。

清理生命周期(clean)

  • pre-clean
  • clean
  • post-clean

默认生命周期(default)

Lifecycle Phase Description
validate Validate the project is correct and all necessary information is available to complete a build
generate-sources Generate any source code for inclusion in compilation
process-sources Process the source code, for example to filter any values
generate-resources Generate resources for inclusion in the package
process-resources Copy and process the resources into the destination directory, ready for packaging
compile Compile the source code of the project
process-classes Post-process the generated files from compilation, for example to do bytecode enhancement on Java classes
generate-test-sources Generate any test source code for inclusion in compilation
process-test-sources Process the test source code, for example to filter any values
generate-test-resources Create resources for testing
process-test-resources Copy and process the resources into the test destination directory
test-compile Compile the test source code into the test destination directory
test Run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed
prepare-package Perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package (coming in Maven 2.1+)
package Take the compiled code and package it in its distributable format, such as a JAR, WAR, or EAR
pre-integration-test Perform actions required before integration tests are executed. This may involve things such as setting up the required environment
integration-test Process and deploy the package if necessary into an environment where integration tests can be run
post-integration-test Perform actions required after integration tests have been executed. This may include cleaning up the environment
verify Run any checks to verify the package is valid and meets quality criteria
install Install the package into the local repository, for use as a dependency in other projects locally
deploy Copies the final package to the remote repository for sharing with other developers and projects (usually only relevant during a formal release)

站点生命周期(site)

  • pre-site
  • site
  • post-site
  • site-deploy

打包Jar生命周期

Lifecycle Phase Goal
process-resources resources:resources
compile compiler:compile
process-test-resources resources:testResources
test-compile compiler:testCompile
test surefire:test
package jar:jar
install install:install
deploy deploy:deploy

常用插件

Maven Dependency插件

分析依赖合理性

$ mvn dependency:analyze
复制代码

列出依赖树结构

$ mvn dependency:tree
复制代码

ps: 检查项目是否使用了 log4j-core可以使用命令 mvn dependency:tree | grep 'log4j-core'

最佳实践

  1. 推荐IDEA插件 "Maven Helper",方便查看依赖树和依赖冲突,指定依赖排除
  1. PRD环境POM禁止依赖SNAPSHOT版本
  • SNAPSHOT类型依赖很不稳定,它随时可能变化
    复制代码
  1. 版本管理使用dependencyManagement
  1. POM文件精简原则移除没有使用的依赖
  •  通过:$ mvn dependency:analyze 命令分析出未使用依赖结果 Unused declared dependencies found:
    复制代码
  1. json推荐Jackson,不推荐fastjson
  1. log推荐slf4j+logback,不推荐log4j-core

加入我们

我们来自字节跳动飞书商业应用研发部(Lark Business Applications),目前我们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了办公区域。我们关注的产品领域主要在企业经验管理软件上,包括飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 领域系统,也包括飞书审批、OA、法务、财务、采购、差旅与报销等系统。欢迎各位加入我们。

扫码发现职位&投递简历

官网投递

job.toutiao.com/s/FyL7DRg

猜你喜欢

转载自juejin.im/post/7101192357080465422