一、 介绍
我们来假设一个场景,目前你打算开始开发一个项目,在决定了改项目使用的框架之后,接下来,你需要到个框架的官网去下载相应的jar包,各种各样的,有可能有冲突的,有可能不是最新的,版本可能需要更新等等之类的问题,这些问题需要花费你大量的时间,进行排查,审核,最终才能运用到项目中,后面的过程中,一旦出现版本更新,版本冲突…
为了解决上述问题,于是,Maven,Ant,Gradle 等优秀的项目管理、构建工具应运而生。本章文章将带你全面的了解以及使用 maven。
二、 本质
maven是基于项目对象构建模型(pom),可以通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。
它也是一套强大的自动化构建工具,覆盖了:编译,测试,运行,清理,打包和部署,它还提供了一个关于仓库的概念,为我们管理项目所依赖的jar包。
<1>、安装
我们从官网下载最新的maven压缩包,并将其解压到我们的某个文件目录下面:maven官网下载地址
解压后的目录如下:
bin: 该目录包含了mvn的运行脚本,我们在cmd命令行中输入的命令就会调用这些脚本。
boot:该目录包含了一个类加载器的框架,maven使用该框架来加载自己的类库。
conf:该目录包含了一些配置文件,例如我们需要更改一些配置的 setting.xml 文件。
lib:该目录包含了maven运行时所包含的所有类库
将maven解压之后,我们还需要配置maven的环境变量,配置方式相对简单,这里不做过多累述。
<2>、Maven的项目目录结构
符合一个maven管理项目的约定目录结构如下:
其中,target目录在每次项目编译的时候会被覆盖,编译,打包之后的文件都将存放在该目录下。
<3>、常用构建命令
我们可以对一个项目进行打包,打包之后的文件将存放于target目录之下,之后使用install命令,将该jar安装到本地仓库中,这样该jar就可以被其他的项目所依赖使用。
maven 引入构建包的流程:当我们通过 "mvn-compile"命令 对项目源代码进行编译时,当程序使用到其他jar包,则程序会到pom.xml文件中查找该依赖jar包的坐标。如果引入该依赖jar坐标,则会根据坐标去本地仓库中查找,如果找不到,则就会去网上的中央仓库中进行查找,如果找到了则会进行下载,下载到本地仓库中,供项目使用,如果都没有,则程序报错。
<4>、仓库
maven 的仓库就是用来存放我们的依赖的,刚接触maven的同学肯定都会学着配置本地的仓库。而该操作的意义就相当于备份我们常用的jar包到本地,在日后其他的项目需要用到时,最快的去获取,而不用再去下载。
如果本地仓库中找不到的构建,则系统会去maven的全球中央仓库中寻找。
中央仓库的地址:
maven根目录 一>lib 一> maven-model-builder-3.6.3.jar 一>\org\apache\maven\model
该目录下会有一个:pom-4.0.0.xml 文件,里面有全球中央仓库的地址:maven中央仓库
maven的中央仓库的服务器都是位于国外的,很多时候由于网络的问题,我们经常无法访问,好在我们国内也有很多maven的镜像仓库,更加的稳定。我们可以通过访问镜像仓库来下载所需要的依赖jar包。
修改镜像仓库地址,在 setting.xml 的 mirrors 标签下添加镜像仓库地址:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
修改本地仓库地址,在 setting.xml 的 settings 标签下添加本地仓库地址:
<localRepository>D:\Maven\MavenRepository</localRepository>
注意,换成自己的地址,不建议存放在C盘。
三、 核心内容
一个完整的项目构建过程包括:清理、编译、测试、打包、集成测试、验证、部署等,Maven 将其进行抽象,实际上,maven和插件是密不可分的。maven抽象出一套项目构建的生命周期,而maven插件则是对maven抽象的具体实现。在官网中提供了很多插件:maven插件
例如 编译 这个过程,就对应了插件: maven-compiler-plugin 插件
<1>、三个生命周期
Maven的三个独立的生命周期:
名称 | 作用 | 任务 |
---|---|---|
clean | 清理项目 | 分为:pre-clean(执行清理前的工作)、clean(清理上一次构建生成的所有文件)、post-clean(执行清理后的文件) |
default (核心) | 构建项目(核心),包含了构建一个项目所需要的步骤 | 如:compile、test、package、install 等核心的阶段 |
site | 生成项目站点,会根据pom中的信息生成一个站点 | pre-site(在生成项目站点前的工作)、site(生成项目的站点文档)、post-site(在生成项目站点后要完成的工作)、site-deploy(发布生成的站点到服务器上) |
每个生命周期是相互独立的,包含不同的阶段。且阶段之间是有顺序的。 执行某个阶段时,其所属的生命周期的前面阶段都会依次执行,但不会触发另外两个生命周期中的任何阶段。
当你运行了 “package” 命令的时候,实际上,clean(清理),compile(编译),test(测试) 都会按顺序执行
<2>、使用插件
插件也同样使用坐标来唯一标识,使用插件也就是在pom.xml文件中,与 “dependencies” 标签同级的地方插入 “build” 标签。
我们使用 source 插件来对项目源码进行打包, 并且我们还希望,当我们执行 “package” 命令时,也可以将源码进行打包,也就是将 source 打包绑定到 package 阶段,代码配置如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<3>、pom.xml 属性详解
pom.xml 的基本结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- Maven版本,固定值 -->
<modelVersion>4.0.0</modelVersion>
<!-- 唯一标识一个坐标 -->
<groupId></groupId> <!-- 项目名,一般使用:反写的公司网址+项目名 -->
<artifactId></artifactId> <!-- 模块名,一般使用:项目名+模块名 -->
<!-- 第一个数字:大版本号,第二个数字:分支版本,第三个数字:小版本号 -->
<version>0.0.1-SNAPSHOT</version>
</project>
版本分为:snapshot(快照)、alpha(内测)、beta(公测)、release(稳定)、GA(发布)
上面是最基本的一个pom.xml结构,正是上面的信息描述了该项目所在maven的世界中的唯一标识(坐标)。
还有部分基本的标签:
<!-- 打包的机制,如pom,jar, maven-plugin, ejb, war, ear, rar, par,默认为jar -->
<packaging>jar</packaging>
<!-- 项目的名称, Maven产生的文档用 -->
<name> xxx-maven </name>
<!-- 项目主页的URL, Maven产生的文档用 -->
<url> http://maven.apache.org </url>
<!-- 定义本项目的依赖关系 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <!-- 依赖范围,有:compile、provided、test、runtime、system、import(2.0.9版本+),用来描述该依赖的作用有效范围 -->
<optional></optional> <!-- 设置依赖是否可选,默认false,表示子项是继承的 -->
<exclusions> <!-- 排除依赖,当jar冲突的时候,用于排除某些依赖jar包 -->
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<!-- 依赖管理,内部可以声明依赖列表,但是不会被引入到实际的依赖中,主要是定义在父模块中,供子模块继承使用 -->
<dependencyManagement></dependencyManagement>
<!-- 构建配置 -->
<build>
<plugins></plugins> <!-- 插件列表 -->
</build>
<parent></parent> <!-- 通常用于在子模块中对父模块的继承 -->
<modules></modules> <!-- 用来聚合运行多个Maven项目 -->
上述标签中其实很多标签都是可以展开的,内容也非常的详细,繁杂,本文篇幅有限,不做过多深入探讨。后面可以新开一个文章详细介绍标签的使用。
<4>、Maven的依赖规则
项目中,我们往往引用到很多不同的依赖,而不同的依赖可能也会依赖到相同依赖的不同版本(绕口多读两遍),而此时,maven对于相同依赖的不同版本的选择则会按照以下规则进行:
>1、短路优先(就近原则)
假如:A依赖B,B依赖C,C依赖X1(jar) :A 一> B 一> C 一>X(1.0版本 jar)
假如:A依赖D,D依赖X2(jar) :A 一> D 一>X(2.0版本 jar)
则,X(2.0版本 jar) 将会被Maven解析
>2、先声明先优先
如果路径相同,则谁先优先声明,谁就将会被解析
实际上,maven的依赖规则也是为了避免jar冲突,但是有些时候现实业务场景往往比较复杂,类似上述第二条规则,实际上并不是一种合理的解决方案,只能说是一种无奈的解决方式。
四、 总结
学会使用 maven对于一个开发来说非常重要,它能够抽象出每个项目为模块的概念,帮助你理解开发如何创造这个世界,改造这个世界,拼装这个世界。上述的内容很多细节都还可以展开,本文作为入门教程,后续也会新增一些关于其他细节深入的研究文章。
欢迎阅读的小伙伴,对于上述内容有错误疑问的地方指正,我会积极回复探讨。