Maven入门

Maven学习笔记

1. 简介

Maven是Apache开源组织的顶级项目之一,是一个项目管理工具。它基于项目对象模型(POM)的概念,主要对项目的构建、依赖、测试、打包、部署、发布、生成站点文档等进行统一管理。

 

2. 下载与安装

我们可以从Apache官网下载Maven的最新版本。下载地址:http://maven.apache.org/download.cgi,Binary是编译后的二进制压缩包,Source是源代码压缩包。

将下载的Binary压缩包解压到任意目录,可以看到有根目录下包含一些子目录和文件。

 

以下是对这些目录的简要说明

目录说明
bin 存放Maven的运行脚本
boot 该目录只有一个文件plexus-classworlds-2.5.1.jar。他是一个类加载器的框架
conf 存放Maven的配置文件,最主要的就是settings.xml文件
lib 包含Maven运行时需要的Java类库以及用到的第三方依赖

3. 基本配置

3.1 配置环境变量

将bin目录配置到环境变量中,方便在终端运行Maven的脚本命令。

macOS或Linux 系统编辑用户下的.bash_profile文件添加MAVEN_HOME并指定根目录的路径,并将MAVEN_HOME追加到PATH中

export MAVEN_HOME=/Users/wangl/apache-maven-3.5.3
export PATH=$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH

 

Windows 系统在用户或系统环境变量中新建MAVEN_HOME并指定根目录路径

MAVEN_HOME=d:\apache-maven-3.5.3

 

然后在path一栏中追加MAVEN_HOME的bin路径

path=...;%MAVEN_HOME%\bin;

 

(备注:可在命令行或者终端使用mvn命令查看Maven版本信息)

mvn -v

 

3.3 配置本地仓库

打开conf目录下的settings.xml配置文件,添加localRepository的配置,指定本地仓库目录的绝对路径。在任意目录下新建文件夹(名称任意),然后将这个文件夹的绝对路径添加到localRespository配置中。

macOS / Linux 系统:

<localRespsitory>/Users/wangl/m2</localRepository>

 

Windows 系统:

<localRespsitory>D:/maven/m2</localRepository>

 

4. 构建Maven项目

4.1 创建简单的Maven项目

在终端使用以下命令来创建一个Maven项目,依据提示选择项目的类型以及填写GAV坐标等信息来完成初始化工作。(注意:第一次构建过程中需要连接网络,Maven会从远程仓库下载相关的插件)

mvn archetype:generate

 

4.2 Maven的项目结构

Maven项目包含以下目录和文件:

目录说明
main/src/java 存放Java源代码的目录,这个目录下存放package以及Java源文件
main/src/resources 存放项目所需的资源文件和配置文件,例如xml、properties文件等
test/src/java 存放单元测试类的源码目录
test/src/resources 存放单元测试所需的资源文件或配置文件
target 用于存放编译后的文件以及Maven打包的文件

4.3 文档对象模型(POM)

每一个Maven项目都包含一个pom.xml(文档对象模型)文件,该文件用于配置当前Maven项目的坐标信息、打包方式、属性、相关依赖、插件、仓库等配置信息,下面列出pom.xml中常用的相关配置。

  1. GAV坐标:通过坐标就能定位到某个具体的Maven项目,由groupId、artifactId、version构成

     

     

  1. 打包方式:表示Maven可将项目打包为jar或war的文件

     

     

  1. 属性配置:主要配置项目的基本属性以及对第三方依赖的版本号统一管理等

     

  1. 依赖:项目所需要的第三方jar文件的依赖配置,配置依赖通常只要填写第三方库的GAV坐标以及作用域。如果不清楚库的坐标,可到https://mvnrepository.com/这个网站中进行查找

     

  1. 插件:配置Maven的相关插件,例如:编译插件、打包插件等等

     

5. Maven仓库

5.1 本地仓库与远程仓库

Maven仓库主要用于存放所有需要依赖的项目以及插件。在Maven中仓库分为远程和本地仓两种。当Maven在查找项目依赖库的时候最先是从本地仓库中查找,如果本地仓库中存在,则直接从本地库中直接依赖,如果本地仓库没有,则访问远程仓库,并从远程仓库下载到本地仓库中再进行依赖。那么当下次再进行相同依赖的时候就不需要从远程仓库中获取了。Maven默认连接了Apache的一个远程中央仓库,仓库名为center。当本地仓库没有相关的项目时,默认就从center的远程中央仓库中查找,这里基本包含的绝大部分的开源项目。我们也可以指定为其他的远程仓库来替换默认的中央仓库。同时,我们开发的Maven项目最终也可以发布到本地或远程仓库中,便于其他项目或者外界对齐进行依赖。

 

5.2 GAV坐标

无论在本地或远程仓库都会存放大量的项目,不同组织可以开发不同的项目,而同一个项目又具有多个不同的版本,那么Maven是如何快速找到相应版本的项目呢?这就是坐标的作用。Maven依据pom文件中配置的groupId(组织)、artifactId(项目名)、version(版本号)来定位到具体的项目,则三个元素就够成了GAV坐标,而这三个元素在仓库都以目录的形式存在。

 

5.3 私服

私服也是远程仓库的一种,私服通常和开发人员的本地仓库在一个局域网中,在一些公司内部都会见到私服的存在,当对外网访问有限制时,那么私服的作用就可以体现出来了。通俗的讲,私服是建立在本地仓库和远程仓库之间,当项目需要依赖其他项目时,先从本地查找,没有则访问私服,如果私服中也没有,那么私服会执行从远程仓库中下载保存到私服中,最后再下拉取到本地仓库。

 

通常构建私服我们会借助第三方的开源组件,最常用的就是Nexus,也是目前主流构建私服的组件。这里不一一描述构建的过程,需要时可以查阅相关文档。下图展示的是Nexus的页面。

 

5.4 镜像仓库

由于网络原因,有时候访问国外的远程仓库比较缓慢,那么这时我们可以使用镜像仓库来配置国内的一些远程仓库。镜像仓库相当于一个拦截器,它会拦截对远程仓库的相关请求,把请求里的地址重定向到镜像中所配置的地址,我们可以在settings.xml配置文件中的mirror节点进行配置,下面将访问默认远程中央仓库(center)的请求重定向到阿里巴巴的远程仓库。

 

id表示镜像的唯一标识符(可自定义),name表示镜像的名称(可自定义),url表示镜像的地址,mirrorOf表示向center中央仓库请求都会重定向到阿里巴巴的仓库中,也可以配置成*号,表示所有请求都重定向到阿里巴巴的仓库中。

6. Maven依赖管理

对于项目中的依赖,Maven会从本地仓库或远程仓库中查找。除此之外,在Maven的依赖管理中还包括依赖的传递、依赖的优先级别、排除依赖、可选依赖、及依赖范围等特点。

6.1 依赖传递性

Maven的依赖是具有传递性的。举个例子,项目中只配置依赖了A,而A又依赖了B,B又依赖了C,那么项目就间接的依赖了B和C,这就是依赖传递性。

A->B->C

 

6.2 依赖传递的优先级别

上面我们知道依赖具有传递性,接下来我们看看下面的情况

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

 

上面A依赖B,B依赖C,C依赖X,同时A又依赖了D,D也依赖了X。如果两个X的版本(version)不一样,一个是1.0和一个2.0,此时Maven会选择哪一个呢?由于只能引入一个版本。因此Maven优先选择最短路径的依赖,也就是A->D->X(2.0)。

另外一种情况就是如果路径长度是一样,像以下的情况

A->B->C->X(1.0)
A->D->E->X(2.0)

 

此时Maven会按照配置顺序选择第一个,也就是A->B->C->X(1.0)。因此我们得出一个结论,当出现依赖同一个项目时,优先选择最短路径的依赖,如果路径长度相同,则按照配置顺序选择最前的进行依赖。

6.4 依赖排除

例如项目中依赖了junit,junit又依赖了hamcrest,如果依赖junit的时候不想连hamcrest也一并加依赖来,那么这时可以在项目中配置junit依赖时排除hamcrest。

 

6.5 可选依赖

上面使用排除的方式是在依赖方排除了junit对hamcrest的依赖,还可以使用另一种方式达到同样效果,就是可选依赖。可选依赖是在被依赖的一方也就是junit在依赖hamcrest时设置optional为true,那么当前项目依赖junit时,也不会间接依赖hamcrest,如果需要用到hamcrest,那么在当前项目需要配置dependency对hamcrest再重新进行依赖。

 

6.6 依赖范围

依赖范围表示依赖的项目在哪些情况下需要一直存在,由依赖配置中的<scope>决定。

 

Maven的依赖范围总共分为5种,compile (编译)、test (测试)、runtime (运行时)、provided(提供者)、system(系统)。如果不指定,则默认为compile。

范围说明
compile 在编译,测试,运行时都需要
test 测试时需要。编译和运行不需要。如Junit
runtime 测试和运行时需要。编译不需要。如JDBC驱动包
provided 编译和测试时需要。运行时不需要,由运行环境提供。如servlet-api
system 本地依赖,不在maven中央仓库

7. Maven生命周期与插件

Maven总共有三套完整的生命周期,每套生命周期会完成一系列的工作。每个生命周期又由多个阶段组成,每个阶段专门负责完成一个具体的操作,例如:清除阶段、编译阶段、打包阶段等等。而每一个阶段的功能都是由一个具体的Maven插件实现的。因此我们也可以理解为整个Maven是由众多的插件所构成。

7.1 Clean生命周期

这套生命周期主要用于清理工作。

阶段说明
pre-clean 执行清理前需要完成的工作
clean 清理上一次构建生成的文件
post-clean 执行清理后需要完成的工作

7.2 Default生命周期

这套生命周期是最核心也是最重要的,从项目的验证、初始化到项目的编译、测试、部署、打包等操作都是在这套生命周期中完成。

阶段说明
validate 验证项目是否正确和所有需要的相关资源是否可用
initialize 初始化构建
generate-sources 为包含在编译范围内的代码生成源代码
process-sources 处理源代码
generate-resources 生成资源文件
compile 编译项目的源代码
process-classes 为编译生成的class文件做后期工作
generate-test-sources 为编译内容生成测试源代码
process-test-source 处理测试源代码
generate-test-resources 生成测试所需的资源文件
process-test-resources 复制并处理资源文件,至目标测试目录
test-compile 编译测试源代码
process-test-classes 为编译生成测试的class文件做后期处理工作
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署
prepare-package 执行打包前的预处理工作
package 接受编译好的代码,打包成可发布的格式,如 jar或war
pre-integration-test 预集成测试
integration-test 按需求将发布包部署到运行测试环境
post-integration-test 执行整合测试
verify 验证包是否有效并符合标准
install 将包安装至本地仓库
deploy 将最终的包发布到远程的仓库

7.3 Site生命周期

这套生命周期用于生成在线文档。

阶段说明
pre-site 执行一些需要在生成站点文档之前完成的工作
site 生成项目的站点文档
post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
site-deploy 将生成的站点文档部署到特定的服务器上

7.4 执行阶段命令

每个阶段的工作都是由一个具体的插件来完成,那么每一个阶段我们也可以当成是一个Maven的命令来执行。例如执行mvn package命令,其实就是调用package相关插件来完成package阶段的工作。

  1. 可以选择某个生命周期中的某个阶段执行,例如:

mvn package

 

这里选择了Default生命周期的package(打包)阶段来执行,需要注意的是,如果选择某个阶段来执行,那么在这个阶段之前的所有阶段都会先执行。

  1. 也可以选择不同生命周期的阶段来混合执行,例如:

mvn clean install

 

这里选择了Clean周期的clean阶段以及Default周期的package阶段来混合执行,同时这两个阶段之前的所有阶段都会先执行。

8. Maven的模块化

目前越来越多的项目采用模块化开发,通过合理的模块拆分,实现代码的复用性,便于维护和管理。尤其在很多的开源框架中也采用多模块的方式,提供插件继承,用户可以根据需要配置指定的模块。Maven的模块化可分为父模块与子模块,它同样具备面向对象思想中的继承和聚合。所有子模块都可以继承自父模块,在父模块中可以聚合(包含)所有的子模块。通常父模块并不包含业务代码的实现,它的作用就是用于包含所有的子模块并统一管理,因此父模块只有一个pom.xml文件,如下图

 

8.1 继承

继承的好处在于所有子模块共性的配置都可以统一放到父模块的pom文件中进行配置,不需要在每个子模块都配置一遍,这也是基于面向对象的思想。(注意:父模块的pom.xml文件中的packaging必须设置为pom)

父模块的pom文件配置:

 

这里将项目的一些属性配置以及junit都放到了父模块中配置。

子模块的pom文件配置:

 

在子模块需在<parent>中配置父模块的GAV坐标,以及在<relativePath>中设置父模块的pom文件的相对路径。而在父模块中配置的属性以及所有的依赖都会被子模块继承下来。子模块需要重新定义'<artifactId>'的名称以及'<packaging>'即可。

8.2 聚合

可以在父模块中聚合所有的子模块,只需要在父模块的pom文件中将所有的子模块配置在<modules>中,<modules>中的每个<module>分别指定各个子模块的名称。如下图

 

 

使用聚合的好处就是当构建整个项目的时候,不需要为每个子模块分别都构建一次,只需要在父模块(相当于整个项目)执行一次构建命令即可。

8.3 按需依赖

有时候并不是所有个子模块都需要继承父类全部的依赖配置,不同的子模块可以根据需要分别继承不同的依赖配置,这时我们就可以在父模块中使用<dependencyManagement>来管理所有依赖配置。如下图

 

上面的junit依赖配置是放在<dependencyManagement>中,这时所有的子模块并不会继承junit的依赖,各个子模块可以根据自身需要再对其进行依赖配置。如下图

 

子模块在依赖时只需要指定groupId和artifactId即可,不需要指定版本号。

 

posted @ 2018-09-05 16:42  黄浩#  阅读(342)  评论(0编辑  收藏  举报