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
$ 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'
最佳实践
- 推荐IDEA插件 "Maven Helper",方便查看依赖树和依赖冲突,指定依赖排除
- PRD环境POM禁止依赖SNAPSHOT版本
-
SNAPSHOT类型依赖很不稳定,它随时可能变化 复制代码
- 版本管理使用dependencyManagement
- POM文件精简原则移除没有使用的依赖
-
通过:$ mvn dependency:analyze 命令分析出未使用依赖结果 Unused declared dependencies found: 复制代码
- json推荐Jackson,不推荐fastjson
- log推荐slf4j+logback,不推荐log4j-core
加入我们
我们来自字节跳动飞书商业应用研发部(Lark Business Applications),目前我们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了办公区域。我们关注的产品领域主要在企业经验管理软件上,包括飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 领域系统,也包括飞书审批、OA、法务、财务、采购、差旅与报销等系统。欢迎各位加入我们。
扫码发现职位&投递简历
官网投递